source: trunk/CrypPlugins/Enigma/Enigma.cs @ 2801

Last change on this file since 2801 was 2801, checked in by weyers, 11 years ago

EnigmaPresentation - EnigmaSettings synchronisation, EnigmaPresentation input management updated

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to URL Author Date Rev Id
File size: 20.8 KB
RevLine 
[691]1/*
2   Copyright 2008-2009, Dr. Arno Wacker, University of Duisburg-Essen
[311]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
17
18using System;
[2801]19using System.Windows;
[311]20using System.Collections.Generic;
21using System.Linq;
22using System.Text;
23
24// additional needed libs
25using System.Windows.Controls;
26using System.ComponentModel;
[353]27using System.Threading;
28using System.Collections;
29using System.Diagnostics;
[691]30using System.IO;
31using System.Reflection;
32using System.Resources;
[311]33
34//Cryptool 2.0 specific includes
[2636]35using Cryptool;
[311]36using Cryptool.PluginBase;
[440]37using Cryptool.PluginBase.Analysis;
[311]38using Cryptool.PluginBase.Cryptography;
39using Cryptool.PluginBase.Miscellaneous;
40using Cryptool.PluginBase.IO;
41
[353]42
[311]43namespace Cryptool.Enigma
44{
[691]45    [Author("Dr. Arno Wacker, Matthäus Wander", "arno.wacker@cryptool.org", "Uni Duisburg-Essen, Fachgebiet Verteilte Systeme", "http://www.vs.uni-due.de")]
[367]46    [PluginInfo(false, "Enigma", "Polyalphabetic rotor-cipher machine", null,
[311]47      "Enigma/Images/Enigma.png", "Enigma/Images/encrypt.png", "Enigma/Images/decrypt.png")]
48    [EncryptionType(EncryptionType.Classic)]
[440]49    public class Enigma: IEncryption, ISpecific
[311]50    {
[423]51        #region Constants
[311]52
[408]53        internal const int ABSOLUTE = 0;
54        internal const int PERCENTAGED = 1;
55        internal const int LOG2 = 2;
[423]56        internal const int SINKOV = 3;
[408]57
[423]58        #endregion
59
60        #region Private variables
61
[311]62        private EnigmaSettings settings;
[2636]63        private EnigmaPresentation myPresentation;
[2801]64        private AutoResetEvent ars;
[353]65        private EnigmaCore core;
66        private EnigmaAnalyzer analyzer;
[311]67        private string inputString;
[423]68        private IDictionary<int, IDictionary<string, double[]>> statistics;
69        // FIXME: enable optional statistics input
70        //private IDictionary<string, double[]> inputTriGrams;
[311]71        private string outputString;
[353]72        private string savedKey;
73       
[311]74        #endregion
75
[353]76        #region Private methods
77
78        #region Formatting stuff
79
80        /// <summary>
81        /// Encrypts or decrypts a string with the given key (rotor positions) and formats
82        /// the output according to the settings
83        /// </summary>
84        /// <param name="rotor1Pos">Position of rotor 1 (fastest)</param>
85        /// <param name="rotor2Pos">Position of rotor 2 (middle)</param>
86        /// <param name="rotor3Pos">Position of rotor 3 (slowest)</param>
87        /// <param name="rotor4Pos">Position of rotor 4 (extra rotor for M4)</param>
88        /// <param name="text">The text for en/decryption. This string may contain
89        /// arbitrary characters, which will be dealt with according to the settings given</param>
90        /// <returns>The encrypted/decrypted string</returns>
91        private string FormattedEncrypt(int rotor1Pos, int rotor2Pos, int rotor3Pos, int rotor4Pos, string text)
92        {
[2701]93            String input = preFormatInput(text);
[2801]94            if (Presentation.IsVisible)
95            {
96               
97                String output = core.Encrypt(rotor1Pos, rotor2Pos, rotor3Pos, rotor4Pos, input);
98               
99                myPresentation.output = output;
100                myPresentation.setinput(input);
101                //myPresentation.playClick(null, EventArgs.Empty);
102                //return postFormatOutput(output);
103                return "";
104            }
105            else
106                return postFormatOutput(core.Encrypt(rotor1Pos, rotor2Pos, rotor3Pos, rotor4Pos, input));
[353]107        }
108
[423]109        internal class UnknownToken
110        {
111            internal string text;
112            internal int position;
113
114            internal UnknownToken(char c, int position)
115            {
116                this.text = char.ToString(c);
117                this.position = position;
118            }
119
120            public override string ToString()
121            {
122                return "[" + text + "," + position + "]";
123            }
124        }
125
126        IList<UnknownToken> unknownList = new List<UnknownToken>();
127
[353]128        /// <summary>
129        /// Format the string to contain only alphabet characters in upper case
130        /// </summary>
131        /// <param name="text">The string to be prepared</param>
132        /// <returns>The properly formated string to be processed direct by the encryption function</returns>
133        private string preFormatInput(string text)
134        {
135            StringBuilder result = new StringBuilder();
[423]136            bool newToken = true;
137            unknownList.Clear();
[353]138
[423]139            for (int i = 0; i < text.Length; i++)
[353]140            {
[423]141                if (settings.Alphabet.Contains(char.ToUpper(text[i])))
[353]142                {
[423]143                    newToken = true;
144                    result.Append(char.ToUpper(text[i])); // FIXME: shall save positions of lowercase letters
145                }
146                else if (settings.UnknownSymbolHandling != 1) // 1 := remove
147                {
148                    // 0 := preserve, 2 := replace by X
149                    char symbol = settings.UnknownSymbolHandling == 0 ? text[i] : 'X';
150
151                    if (newToken)
[353]152                    {
[423]153                        unknownList.Add(new UnknownToken(symbol, i));
154                        newToken = false;
[353]155                    }
[423]156                    else
157                    {
158                        unknownList.Last().text += symbol;
159                    }
[353]160                }
161            }
162
[423]163            return result.ToString().ToUpper();
[353]164
165        }
166
[423]167        //// legacy code
168        //switch (settings.UnknownSymbolHandling)
169        //{
170        //    case 0: // ignore
171        //        result.Append(c);
172        //        break;
173        //    case 1: // remove
174        //        continue;
175        //    case 2: // replace by X
176        //        result.Append('X');
177        //        break;
178        //}
179
[353]180        /// <summary>
181        /// Formats the string processed by the encryption for presentation according
182        /// to the settings given
183        /// </summary>
184        /// <param name="text">The encrypted text</param>
185        /// <returns>The formatted text for output</returns>
186        private string postFormatOutput(string text)
187        {
[423]188            StringBuilder workstring = new StringBuilder(text);
189            foreach (UnknownToken token in unknownList)
190            {
191                workstring.Insert(token.position, token.text);
192            }
193
[353]194            switch (settings.CaseHandling)
195            {
[423]196                default:
[353]197                case 0: // preserve
[423]198                    // FIXME: shall restore lowercase letters
199                    return workstring.ToString();
[353]200                case 1: // upper
[423]201                    return workstring.ToString().ToUpper();
[353]202                case 2: // lower
[423]203                    return workstring.ToString().ToLower();
[353]204            }
205        }
206
207        #endregion
208
[354]209        #region Analyzer event handler
[353]210
211        /// <summary>
[423]212        /// This eventhandler is called, when the analyzer has an intermediate result
[353]213        /// </summary>
214        /// <param name="sender"></param>
215        /// <param name="e"></param>
216        private void analyzer_OnIntermediateResult(object sender, IntermediateResultEventArgs e)
217        {
218            // Got an intermidate results from the analyzer, hence display it
[423]219            outputString = postFormatOutput(e.Result);
[353]220            OnPropertyChanged("OutputString");
221        }
222
223        #endregion
224
[408]225        #region n-gram frequencies
226
227        private IDictionary<string, double[]> LoadDefaultStatistics(int length)
228        {
[423]229            Dictionary<string, double[]> grams = new Dictionary<string, double[]>();
[408]230
[975]231            StreamReader reader = new StreamReader(Path.Combine(DirectoryHelper.DirectoryCrypPlugins, GetStatisticsFilename(length)));
[408]232
233            string line;
234            while ((line = reader.ReadLine()) != null)
235            {
236                if (line.StartsWith("#"))
237                    continue;
238
[510]239                string[] tokens = WordTokenizer.tokenize(line).ToArray();
[423]240                if (tokens.Length == 0)
241                    continue;
[408]242                Debug.Assert(tokens.Length == 2, "Expected 2 tokens, found " + tokens.Length + " on one line");
243
[423]244                grams.Add(tokens[0], new double[] { Double.Parse(tokens[1]), 0, 0, 0 });
[408]245            }
246
247            double sum = grams.Values.Sum(item => item[ABSOLUTE]);
248            LogMessage("Sum of all n-gram counts is: " + sum, NotificationLevel.Debug);
249
250            // calculate scaled values
251            foreach (double[] g in grams.Values)
252            {
253                g[PERCENTAGED] = g[ABSOLUTE] / sum;
254                g[LOG2] = Math.Log(g[ABSOLUTE], 2);
[423]255                g[SINKOV] = Math.Log(g[PERCENTAGED], Math.E);
[408]256            }
257
258            return grams;
259        }
260
261        /// <summary>
262        /// Get file name for default n-gram frequencies.
263        /// </summary>
264        /// <param name="length"></param>
265        /// <exception cref="NotSupportedException">No default n-gram frequencies available</exception>
266        /// <returns></returns>
267        private string GetStatisticsFilename(int length)
268        {
[423]269            if (length < 1)
[408]270            {
[423]271                throw new ArgumentOutOfRangeException("There is no known default statistic for an n-gram length of " + length);
[408]272            }
[423]273
274            return "Enigma_" + length + "gram_Frequency.txt";
[408]275        }
276
[353]277        #endregion
278
[408]279        #endregion
[311]280
[408]281        #region Constructor
282
[311]283        public Enigma()
284        {
285            this.settings = new EnigmaSettings();
[353]286            this.core = new EnigmaCore(this);
287            this.analyzer = new EnigmaAnalyzer(this);
288            this.analyzer.OnIntermediateResult += new EventHandler<IntermediateResultEventArgs>(analyzer_OnIntermediateResult);
[423]289            this.statistics = new Dictionary<int, IDictionary<string, double[]>>();
[2801]290           
291            this.ars = new AutoResetEvent(false);
292            this.myPresentation = new EnigmaPresentation(this);
[2636]293            this.Presentation = myPresentation;
[2801]294            //this.Presentation.IsVisibleChanged += presentation_isvisibleChanged;
[2636]295            this.settings.PropertyChanged += myPresentation.settings_OnPropertyChange;
296            this.settings.PropertyChanged += settings_OnPropertyChange;
[2801]297            this.myPresentation.fireLetters += fireLetters;
[311]298
[2801]299           
300            }
301
[311]302        #endregion
303
304        #region Events
305
[353]306#pragma warning disable 67
[311]307        public event StatusChangedEventHandler OnPluginStatusChanged;
[353]308#pragma warning restore
[311]309        public event GuiLogNotificationEventHandler OnGuiLogNotificationOccured;
310        public event PluginProgressChangedEventHandler OnPluginProgressChanged;
[2636]311
[2801]312        private void fireLetters(object sender, EventArgs args) 
313        {
314            Object[] carrier = sender as Object[];
315
316            OutputString = (String)carrier[0] ;
317            int x = (int)carrier[1];
318            int y = (int)carrier[2];
319            ShowProgress(x,y);
320
321        }
322
323        private void presentation_isvisibleChanged(object sender, DependencyPropertyChangedEventArgs args) 
324        {
325            LogMessage("Here we go " + args.NewValue, NotificationLevel.Debug);
326            Boolean visible = (Boolean) args.NewValue ;
327            if (visible)
328            {
329             
330            }
331
332            else 
333            { 
334               
335            }
336        }
337
[2636]338        private void settings_OnPropertyChange(object sender, PropertyChangedEventArgs e)
339        {
340            EnigmaSettings dummyset = sender as EnigmaSettings;
341            //myPresentation.settingsChanged(dummyset);
342           
343           
344            LogMessage("OnPropertyChange " + e.PropertyName, NotificationLevel.Debug);
345        }
346
[311]347        #endregion
348
349        #region IPlugin properties
350
351        public ISettings Settings
352        {
353            get { return this.settings; }
354        }
355
356        public UserControl Presentation
357        {
[2636]358            get;
359            private set;
[311]360        }
361
362        public UserControl QuickWatchPresentation
363        {
[2636]364            get { return Presentation; }
[311]365        }
366
367        #endregion
368
369        #region Connector properties
370
[2334]371        [PropertyInfo(Direction.InputData, "Text input", "Input a string to be processed by the Enigma machine", "", true, false, QuickWatchFormat.Text, null)]
[311]372        public string InputString
373        {
374            get { return this.inputString; }
375            set
376            {
377                if (value != inputString)
378                {
379                    this.inputString = value;
380                    OnPropertyChanged("InputString");
381                }
382            }
383        }
384
[2334]385        //[PropertyInfo(Direction.InputData, "n-gram dictionary", "Dictionary with gram counts (string -> [absolute, percentaged, log2])", "", false, false, QuickWatchFormat.Text, "FrequencyTest.QuickWatchDictionary")]
[423]386        //public IDictionary<string, double[]> InputGrams
387        //{
388        //    get { return this.inputTriGrams; }
389        //    set
390        //    {
391        //        if (value != inputTriGrams)
392        //        {
393        //            this.inputTriGrams = value;
394        //            OnPropertyChanged("InputTriGrams");
395        //        }
396        //    }
397        //}
[404]398
[2334]399        [PropertyInfo(Direction.OutputData, "Text output", "The string after processing with the Enigma machine", "", false, false, QuickWatchFormat.Text, null)]
[311]400        public string OutputString
401        {
402            get { return this.outputString; }
403            set
404            {
405                outputString = value;
406                OnPropertyChanged("OutputString");
407            }
408        }
409
410        #endregion
411
412        #region Public methods
413
414        public void PreExecution()
415        {
[2801]416            myPresentation.stopclick(this, EventArgs.Empty);
[311]417            EventsHelper.GuiLogMessage(OnGuiLogNotificationOccured, this, new GuiLogEventArgs("Preparing enigma for operation..", this,  NotificationLevel.Info));
418
419            if (settings.Model != 3)
420            {
421                EventsHelper.GuiLogMessage(OnGuiLogNotificationOccured, this, new GuiLogEventArgs("This simulator is work in progress. As of right now only Enigma I is supported!!", this, NotificationLevel.Warning));
422                return;
423            }
424
[353]425            // remember the current key-setting, in order to restore on stop
426            savedKey = settings.Key;
427
[311]428            //configure the enigma
[353]429            core.setInternalConfig(settings.Rotor1, settings.Rotor2, settings.Rotor3, settings.Rotor4,
430                        settings.Reflector, settings.Ring1, settings.Ring2, settings.Ring3, settings.Ring4,
431                        settings.PlugBoard);
[311]432        }
433
434        public void Execute()
435        {
[353]436            if (inputString == null)
437                return;
[311]438
[353]439
[311]440            if (settings.Model != 3)
441            {
[353]442                LogMessage("This simulator is work in progress. As of right now only Enigma I is supported!!", NotificationLevel.Error);
[311]443                return;
444            }
445
[353]446           
[311]447
[353]448            switch (settings.Action)
[311]449            {
[353]450                case 0:
451                    LogMessage("Enigma encryption/decryption started...", NotificationLevel.Info);
[311]452
[353]453                    // re-set the key, in case we get executed again during single run
[614]454                    settings.Key = savedKey.ToUpper();
[311]455
[353]456                    // do the encryption
457                    outputString = FormattedEncrypt(settings.Alphabet.IndexOf(settings.Key[2]), 
458                        settings.Alphabet.IndexOf(settings.Key[1]),
459                        settings.Alphabet.IndexOf(settings.Key[0]), 
460                        0, inputString);
[311]461
462
[423]463                    // FIXME: output all scorings
[1843]464                    LogMessage("Enigma encryption done. The resulting index of coincidences is " + analyzer.calculateScore(outputString, 0), NotificationLevel.Info);
[311]465
[353]466                    // "fire" the output
467                    OnPropertyChanged("OutputString");
468                    break;
469                case 1:
470                    LogMessage("Enigma analysis starting ...", NotificationLevel.Info);
[311]471
[353]472                    //prepare for analysis
473                    LogMessage("ANALYSIS: Preformatting text...", NotificationLevel.Debug);
474                    string preformatedText = preFormatInput(inputString);
[311]475
[353]476                    // perform the analysis
[423]477                    outputString = postFormatOutput(analyzer.Analyze(preformatedText));
[353]478                    OnPropertyChanged("OutputString");
[311]479
[353]480                    ShowProgress(1000, 1000);
481                    break;
482                default:
483                    break;
484            }
[311]485
486        }
487
[353]488        public void PostExecution()
[311]489        {
[353]490            LogMessage("Enigma shutting down. Reverting key to inial value!", NotificationLevel.Info);
[740]491            if (savedKey != null && savedKey.Length > 0)
[311]492            {
[353]493                settings.Key = savedKey; // re-set the key
[311]494            }
495           
[353]496        }
[311]497
[353]498        public void Pause()
499        {
500            LogMessage("The \"Pause\"-Feature is not implemented!", NotificationLevel.Warning);
501        }
[311]502
[353]503        public void Stop()
504        {
505            LogMessage("Enigma stopped", NotificationLevel.Info);
[2701]506            myPresentation.stopclick(this, EventArgs.Empty);
[353]507            analyzer.StopAnalysis();
[311]508        }
509
[353]510        public void Initialize()
[311]511        {
[945]512            LogMessage("Initializing..", NotificationLevel.Debug);
513            this.settings.Initialize();
[311]514        }
515
[353]516        public void Dispose()
[311]517        {
[353]518            LogMessage("Dispose", NotificationLevel.Debug);
519        }
[311]520
521
522
[353]523        /// <summary>
524        /// Logs a message to the Cryptool console
525        /// </summary>
526        public void LogMessage(string msg, NotificationLevel level)
527        {
528            EventsHelper.GuiLogMessage(OnGuiLogNotificationOccured, this, new GuiLogEventArgs(msg, this, level));
529        }
[311]530
[353]531        /// <summary>
532        /// Sets the progress bar for this plugin
533        /// </summary>
534        /// <param name="val"></param>
535        /// <param name="max"></param>
536        public void ShowProgress(double val, double max)
537        {
538            EventsHelper.ProgressChanged(OnPluginProgressChanged, this, new PluginProgressEventArgs(val, max));
[311]539        }
540
[353]541        /// <summary>
542        /// Returns a formated string with all plugs from a given substitution string
543        /// This method should be move to some more adequate place
544        /// </summary>
545        /// <param name="pb">The substitution string for a plugboard</param>
546        /// <returns>A list of plugs</returns>
547        public string pB2String(string pb)
[311]548        {
[353]549            if (pb.Length != settings.Alphabet.Length)
550                return "-- no plugs --";
[311]551
552
[353]553            StringBuilder result = new StringBuilder();
[311]554
[353]555            for (int i = 0; i < settings.Alphabet.Length; i++)
[311]556            {
[353]557                if (settings.Alphabet[i] != pb[i] && !result.ToString().Contains(settings.Alphabet[i]))
558                {
559                    if (result.Length > 0)
560                        result.Append(' ');
561
562                    result.Append(settings.Alphabet[i].ToString() + pb[i].ToString());
563                }
[311]564            }
565
[353]566            if (result.Length == 0)
567                result.Append("-- no plugs --");
[311]568
[353]569            return result.ToString();
[311]570        }
571
[423]572        public IDictionary<string, double[]> GetStatistics(int gramLength)
573        {
574            // FIXME: inputTriGrams is not being used!
575
576            // FIXME: implement exception handling
577
578            if (!statistics.ContainsKey(gramLength))
579            {
580                LogMessage("Trying to load default statistics for " + gramLength + "-grams", NotificationLevel.Info);
581                statistics[gramLength] = LoadDefaultStatistics(gramLength);
582            }
583
584            return statistics[gramLength];
585        }
586
[311]587        #endregion
588
589        #region INotifyPropertyChanged Member
590
591        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
592
593        public void OnPropertyChanged(string name)
594        {
595            EventsHelper.PropertyChanged(PropertyChanged, this, new PropertyChangedEventArgs(name));
596        }
597
598        #endregion
599    }
600}
Note: See TracBrowser for help on using the repository browser.