source: trunk/CrypPlugins/KeySearcher/KeySearcher.cs @ 1069

Last change on this file since 1069 was 1069, checked in by arnold, 12 years ago

some little changes

File size: 29.0 KB
Line 
1using System;
2using System.Linq;
3using System.Text;
4using Cryptool.PluginBase.Analysis;
5using Cryptool.PluginBase;
6using System.Windows.Controls;
7using System.ComponentModel;
8using Cryptool.PluginBase.Control;
9using System.Collections;
10using System.Collections.Generic;
11using System.Threading;
12using System.Windows.Threading;
13using Cryptool.PluginBase.Miscellaneous;
14using System.IO;
15
16namespace KeySearcher
17{   
18    [Author("Sven Rech, Nils Kopal, Raoul Falk, Dennis Nolte", "rech@cryptool.org", "Uni Duisburg-Essen", "http://www.uni-due.de")]
19    [PluginInfo(true, "KeySearcher", "Bruteforces a decryption algorithm.", "KeySearcher/DetailedDescription/Description.xaml", "KeySearcher/Images/icon.png")]
20    public class KeySearcher : IAnalysisMisc
21    {
22        /// <summary>
23        /// used for creating the TopList
24        /// </summary>
25        private Queue valuequeue;
26        private double value_threshold;
27        /// <summary>
28        /// the thread with the most keys left
29        /// </summary>
30        private int maxThread;
31        private Mutex maxThreadMutex = new Mutex();
32
33        private KeyPattern pattern = null;
34        public KeyPattern Pattern
35        {
36            get
37            {
38                return pattern;
39            }
40            set
41            {
42                pattern = value;
43                if ((settings.Key == null) || ((settings.Key != null) && !pattern.testWildcardKey(settings.Key)))
44                    settings.Key = pattern.giveInputPattern();
45            }
46        }
47
48        private bool stop;
49
50        #region IControlEncryption Members
51
52        private IControlEncryption controlMaster;
53        [PropertyInfo(Direction.ControlMaster, "Control Master", "Used for bruteforcing", "", DisplayLevel.Beginner)]
54        public IControlEncryption ControlMaster
55        {
56            get { return controlMaster; }
57            set
58            {
59                if (controlMaster != null)
60                {
61                    controlMaster.keyPatternChanged -= keyPatternChanged;
62                    controlMaster.OnStatusChanged -= onStatusChanged;
63                }
64                if (value != null)
65                {
66                    Pattern = new KeyPattern(value.getKeyPattern());
67                    value.keyPatternChanged += keyPatternChanged;
68                    value.OnStatusChanged += onStatusChanged;
69                    controlMaster = value;
70                    OnPropertyChanged("ControlMaster");
71
72                }
73                else
74                    controlMaster = null;
75            }
76        }
77
78        #endregion
79
80        #region IControlCost Members
81
82        private IControlCost costMaster;
83        [PropertyInfo(Direction.ControlMaster, "Cost Master", "Used for cost calculation", "", DisplayLevel.Beginner)]
84        public IControlCost CostMaster
85        {
86            get { return costMaster; }
87            set
88            {
89                costMaster = value;
90            }
91        }
92
93        #endregion
94
95        #region IPlugin Members
96
97        public event StatusChangedEventHandler OnPluginStatusChanged;
98
99        public event GuiLogNotificationEventHandler OnGuiLogNotificationOccured;
100
101        public event PluginProgressChangedEventHandler OnPluginProgressChanged;
102
103        private KeySearcherSettings settings;
104
105        public KeySearcher()
106        {
107            settings = new KeySearcherSettings(this);
108            QuickWatchPresentation = new KeySearcherQuickWatchPresentation();
109           
110        }
111
112        public ISettings Settings
113        {
114            get { return settings; }
115        }
116
117        public UserControl Presentation
118        {
119            get { return QuickWatchPresentation; }
120        }
121
122        public UserControl QuickWatchPresentation
123        {
124            get;
125            private set;
126        }
127
128        public void PreExecution()
129        {
130        }
131
132        public void Execute()
133        {
134        }
135
136        public void PostExecution()
137        {
138        }
139
140        public void Pause()
141        {
142        }
143
144        public void Stop()
145        {
146            stop = true;
147        }
148
149        public void Initialize()
150        {
151        }
152
153        public void Dispose()
154        {
155        }
156
157        #endregion
158
159        #region INotifyPropertyChanged Members
160
161        public event PropertyChangedEventHandler PropertyChanged;
162
163        public void OnPropertyChanged(string name)
164        {
165            if (PropertyChanged != null)
166            {
167                PropertyChanged(this, new PropertyChangedEventArgs(name));
168            }
169        }
170
171        #endregion
172
173        /* BEGIN functionality */
174
175        #region whole KeySearcher functionality
176
177        private class ThreadStackElement
178        {
179            public AutoResetEvent ev;
180            public int threadid;
181        }
182
183        #region code for the worker threads
184
185        private void KeySearcherJob(object param)
186        {
187            object[] parameters = (object[])param;
188            KeyPattern[] patterns = (KeyPattern[])parameters[0];
189            int threadid = (int)parameters[1];
190            BigInteger[] doneKeysArray = (BigInteger[])parameters[2];
191            BigInteger[] keycounterArray = (BigInteger[])parameters[3];
192            BigInteger[] keysLeft = (BigInteger[])parameters[4];
193            IControlEncryption sender = (IControlEncryption)parameters[5];
194            int bytesToUse = (int)parameters[6];
195            Stack threadStack = (Stack)parameters[7];
196
197            KeyPattern pattern = patterns[threadid];
198
199            bool useKeyblocks = true;
200
201            try
202            {
203                while (pattern != null)
204                {
205                    BigInteger size = pattern.size();
206                    keysLeft[threadid] = size;
207                    int nextWildcard;
208
209                    do
210                    {
211                        //if we are the thread with most keys left, we have to share them:
212                        if (maxThread == threadid && threadStack.Count != 0)
213                        {
214                            maxThreadMutex.WaitOne();
215                            if (maxThread == threadid && threadStack.Count != 0)
216                            {
217                                try
218                                {
219                                    KeyPattern[] split = pattern.split();
220                                    patterns[threadid] = split[0];
221                                    pattern = split[0];
222                                    ThreadStackElement elem = (ThreadStackElement)threadStack.Pop();
223                                    patterns[elem.threadid] = split[1];
224                                    elem.ev.Set();    //wake the other thread up                                   
225                                    size = pattern.size();
226                                    keysLeft[threadid] = size;
227                                }
228                                catch (Exception e)
229                                {
230                                    //pattern can't be split? who cares :)
231                                }
232                                maxThread = -1;
233                            }
234                            maxThreadMutex.ReleaseMutex();
235                        }
236
237
238                        ValueKey valueKey = new ValueKey();
239                        int blocksize = 0;
240                        nextWildcard = -3;
241                        try
242                        {
243                            string key = "";
244                            if (useKeyblocks)
245                                key = pattern.getKeyBlock(ref blocksize, ref nextWildcard);
246                            if (key == null)
247                                useKeyblocks = false;
248                            if (!useKeyblocks)
249                                key = pattern.getKey();
250                            valueKey.key = key;
251                        }
252                        catch (Exception ex)
253                        {
254                            GuiLogMessage("Could not get next key: " + ex.Message, NotificationLevel.Error);
255                            return;
256                        }
257
258                        int[] arrayPointers = null;
259                        int[] arraySuccessors = null;
260                        int[] arrayUppers = null;
261                        byte[] keya = ControlMaster.getKeyFromString(valueKey.key, ref arrayPointers, ref arraySuccessors, ref arrayUppers);
262                        if (keya == null)
263                        {
264                            useKeyblocks = false;
265                            nextWildcard = -2;
266                            continue;   //try again
267                        }
268
269                        if (arrayPointers == null)  //decrypt only one key
270                        {
271                            if (!decryptAndCalculate(sender, bytesToUse, ref valueKey, keya, 0, null))
272                                return;
273                            doneKeysArray[threadid]++;
274                            keycounterArray[threadid]++;
275                            keysLeft[threadid]--;
276                        }
277                        else  //decrypt several keys
278                        {
279                            int counter = 0;
280                            if (!bruteforceBlock(sender, bytesToUse, ref valueKey, keya, arrayPointers, arraySuccessors, arrayUppers, 0, ref counter, pattern))
281                                return;
282                            doneKeysArray[threadid] += blocksize;
283                            keycounterArray[threadid] += blocksize;
284                            keysLeft[threadid] -= blocksize;
285                        }
286                    } while (pattern.nextKey(nextWildcard) && !stop);
287
288                    if (stop)
289                        return;
290
291                    //Let's wait until another thread is willing to share with us:
292                    pattern = null;
293                    ThreadStackElement el = new ThreadStackElement();
294                    el.ev = new AutoResetEvent(false);
295                    el.threadid = threadid;
296                    patterns[threadid] = null;
297                    threadStack.Push(el);
298                    GuiLogMessage("Thread waiting for new keys.", NotificationLevel.Debug);
299                    el.ev.WaitOne();
300                    GuiLogMessage("Thread waking up with new keys.", NotificationLevel.Debug);
301                    pattern = patterns[threadid];
302                }
303            }
304            finally
305            {
306                sender.Dispose();
307            }
308        }
309
310        #region bruteforce methods
311
312        private bool bruteforceBlock(IControlEncryption sender, int bytesToUse, ref ValueKey valueKey, byte[] keya, int[] arrayPointers,
313            int[] arraySuccessors, int[] arrayUppers, int arrayPointer, ref int counter, KeyPattern pattern)
314        {
315            byte store = keya[arrayPointers[arrayPointer]];
316            while (!stop)
317            {
318                if (arrayPointer + 1 < arrayPointers.Length && arrayPointers[arrayPointer + 1] != -1)
319                {
320                    if (!bruteforceBlock(sender, bytesToUse, ref valueKey, keya, arrayPointers, arraySuccessors, arrayUppers, arrayPointer + 1, ref counter, pattern))
321                        return false;
322                }
323                else
324                {
325                    if (!decryptAndCalculate(sender, bytesToUse, ref valueKey, keya, counter, pattern))
326                        return false;
327                }
328
329                if (keya[arrayPointers[arrayPointer]] + arraySuccessors[arrayPointer] <= arrayUppers[arrayPointer])
330                {
331                    keya[arrayPointers[arrayPointer]] += (byte)arraySuccessors[arrayPointer];
332                    counter++;
333                }
334                else
335                    break;
336            }
337            keya[arrayPointers[arrayPointer]] = store;
338            if (stop)
339                return false;
340            return true;
341        }
342
343        private bool decryptAndCalculate(IControlEncryption sender, int bytesToUse, ref ValueKey valueKey, byte[] keya, int counter, KeyPattern pattern)
344        {
345            try
346            {
347                valueKey.decryption = sender.Decrypt(keya, bytesToUse);
348            }
349            catch (Exception ex)
350            {
351                GuiLogMessage("Decryption is not possible: " + ex.Message, NotificationLevel.Error);
352                GuiLogMessage("Stack Trace: " + ex.StackTrace, NotificationLevel.Error);
353                return false;
354            }
355
356            try
357            {
358                valueKey.value = CostMaster.calculateCost(valueKey.decryption);
359            }
360            catch (Exception ex)
361            {
362                GuiLogMessage("Cost calculation is not possible: " + ex.Message, NotificationLevel.Error);
363                return false;
364            }
365
366            if (this.costMaster.getRelationOperator() == RelationOperator.LargerThen)
367            {
368                if (valueKey.value > value_threshold)
369                {
370                    if (pattern != null)
371                        valueKey.key = pattern.getKey(counter);
372                    valuequeue.Enqueue(valueKey);
373                }
374            }
375            else
376            {
377                if (valueKey.value < value_threshold)
378                {
379                    if (pattern != null)
380                        valueKey.key = pattern.getKey(counter);
381                    valuequeue.Enqueue(valueKey);
382                }
383            }
384            return true;
385        }
386
387        #endregion
388
389        #endregion
390
391        public void process(IControlEncryption sender)
392        {
393            if (sender == null || costMaster == null)
394                return;
395            if (!Pattern.testWildcardKey(settings.Key))
396            {
397                GuiLogMessage("Wrong key pattern!", NotificationLevel.Error);
398                return;
399            }
400            Pattern.WildcardKey = settings.Key;
401            bruteforcePattern(Pattern, sender);
402        }
403
404        // modified by Christian Arnold 2009.12.07 - return type LinkedList (top10List)
405        // main entry point to the KeySearcher
406        private LinkedList<ValueKey> bruteforcePattern(KeyPattern pattern, IControlEncryption sender)
407        {
408            int maxInList = 10;
409            LinkedList<ValueKey> costList = new LinkedList<ValueKey>();
410            fillListWithDummies(maxInList, costList);
411
412            stop = false;
413            if (!pattern.testWildcardKey(settings.Key))
414            {
415                GuiLogMessage("Wrong key pattern!", NotificationLevel.Error);
416                return null;
417            }
418
419            int bytesToUse = 0;
420
421            try
422            {
423                bytesToUse = CostMaster.getBytesToUse();
424            }
425            catch (Exception ex)
426            {
427                GuiLogMessage("Bytes used not valid: " + ex.Message, NotificationLevel.Error);
428                return null;
429            }
430
431            BigInteger size = pattern.size();
432            KeyPattern[] patterns = splitPatternForThreads(pattern);
433
434            valuequeue = Queue.Synchronized(new Queue());
435
436            BigInteger[] doneKeysA = new BigInteger[patterns.Length];
437            BigInteger[] keycounters = new BigInteger[patterns.Length];
438            BigInteger[] keysleft = new BigInteger[patterns.Length];
439            Stack threadStack = Stack.Synchronized(new Stack());
440            startThreads(sender, bytesToUse, patterns, doneKeysA, keycounters, keysleft, threadStack);
441
442            //update message:
443            while (!stop)
444            {
445                Thread.Sleep(1000);
446
447                updateToplist(costList);
448
449                #region calculate global counters from local counters
450                BigInteger keycounter = 0;
451                BigInteger doneKeys = 0;
452                foreach (BigInteger dk in doneKeysA)
453                    doneKeys += dk;
454                foreach (BigInteger kc in keycounters)
455                    keycounter += kc;
456                #endregion
457
458                if (keycounter > size)
459                    GuiLogMessage("There must be an error, because we bruteforced too much keys...", NotificationLevel.Error);
460
461                #region determination of the thread with most keys
462                if (size - keycounter > 1000)
463                {
464                    maxThreadMutex.WaitOne();
465                    BigInteger max = 0;
466                    int id = -1;
467                    for (int i = 0; i < patterns.Length; i++)
468                        if (keysleft[i] != null && keysleft[i] > max)
469                        {
470                            max = keysleft[i];
471                            id = i;
472                        }
473                    maxThread = id;
474                    maxThreadMutex.ReleaseMutex();
475                }
476                #endregion
477
478                showProgress(costList, size, keycounter, doneKeys);
479
480                #region set doneKeys to 0
481                doneKeys = 0;
482                for (int i = 0; i < doneKeysA.Length; i++)
483                    doneKeysA[i] = 0;
484                #endregion
485
486                if (keycounter >= size)
487                    break;
488            }//end while
489
490            //wake up all sleeping threads, so they can stop:
491            while (threadStack.Count != 0)
492                ((ThreadStackElement)threadStack.Pop()).ev.Set();
493
494            if (!stop)
495                ProgressChanged(1, 1);
496
497            return costList;
498        }
499
500        private void showProgress(LinkedList<ValueKey> costList, BigInteger size, BigInteger keycounter, BigInteger doneKeys)
501        {
502            System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
503
504            LinkedListNode<ValueKey> linkedListNode;
505            ProgressChanged(Math.Pow(10, keycounter.log(10) - size.log(10)), 1.0);
506
507            if (QuickWatchPresentation.IsVisible && doneKeys != 0 && !stop)
508            {
509                double time = (Math.Pow(10, (size - keycounter).log(10) - doneKeys.log(10)));
510                TimeSpan timeleft = new TimeSpan(-1);
511
512                try
513                {
514                    if (time / (24 * 60 * 60) <= int.MaxValue)
515                    {
516                        int days = (int)(time / (24 * 60 * 60));
517                        time = time - (days * 24 * 60 * 60);
518                        int hours = (int)(time / (60 * 60));
519                        time = time - (hours * 60 * 60);
520                        int minutes = (int)(time / 60);
521                        time = time - (minutes * 60);
522                        int seconds = (int)time;
523
524                        timeleft = new TimeSpan(days, hours, minutes, (int)seconds, 0);
525                    }
526                }
527                catch
528                {
529                    //can not calculate time span
530                }
531
532                ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).Dispatcher.Invoke(DispatcherPriority.Normal, (SendOrPostCallback)delegate
533                {
534                    ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).keysPerSecond.Text = "" + doneKeys;
535                    if (timeleft != new TimeSpan(-1))
536                    {
537                        ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).timeLeft.Text = "" + timeleft;
538                        try
539                        {
540                            ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).endTime.Text = "" + DateTime.Now.Add(timeleft);
541                        }
542                        catch
543                        {
544                            ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).endTime.Text = "in a galaxy far, far away...";
545                        }
546                    }
547                    else
548                    {
549                        ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).timeLeft.Text = "incalculable :-)";
550                        ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).endTime.Text = "in a galaxy far, far away...";
551                    }
552
553                    ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).entries.Clear();
554                    linkedListNode = costList.First;
555                   
556                    int i = 0;
557                    while (linkedListNode != null)
558                    {
559                        i++;
560
561                        ResultEntry entry = new ResultEntry();
562                        entry.Ranking = "" + i;
563                        entry.Value = "" + Math.Round(linkedListNode.Value.value,3);
564                        entry.Key = linkedListNode.Value.key;
565                        entry.Text = enc.GetString(linkedListNode.Value.decryption);
566
567                        ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).entries.Add(entry);
568                        linkedListNode = linkedListNode.Next;
569                    }
570                }
571                , null);
572            }//end if
573
574
575            if (!stop && QuickWatchPresentation.IsVisible)
576            {
577
578                ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).Dispatcher.Invoke(DispatcherPriority.Normal, (SendOrPostCallback)delegate
579                {
580                    ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).entries.Clear();
581                    linkedListNode = costList.First;                   
582                    int i = 0;
583
584                    while (linkedListNode != null)
585                    {
586                        i++;
587
588                        ResultEntry entry = new ResultEntry();
589                        entry.Ranking = "" + i;
590                        entry.Value = "" + Math.Round(linkedListNode.Value.value, 3);
591                        entry.Key = linkedListNode.Value.key;
592                        entry.Text = enc.GetString(linkedListNode.Value.decryption);
593
594                        ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).entries.Add(entry);
595                        linkedListNode = linkedListNode.Next;
596                    }
597                }
598                , null);
599            }
600        }
601
602        #region For TopList
603
604        private void fillListWithDummies(int maxInList, LinkedList<ValueKey> costList)
605        {
606            ValueKey valueKey = new ValueKey();
607            if (this.costMaster.getRelationOperator() == RelationOperator.LessThen)
608                valueKey.value = double.MaxValue;
609            else
610                valueKey.value = double.MinValue;
611            valueKey.key = "dummykey";
612            valueKey.decryption = new byte[0];
613            value_threshold = valueKey.value;
614            LinkedListNode<ValueKey> node = costList.AddFirst(valueKey);
615            for (int i = 1; i < maxInList; i++)
616            {
617                node = costList.AddAfter(node, valueKey);
618            }
619        }
620
621        private void updateToplist(LinkedList<ValueKey> costList)
622        {
623            LinkedListNode<ValueKey> node;
624            while (valuequeue.Count != 0)
625            {
626                ValueKey vk = (ValueKey)valuequeue.Dequeue();
627                if (this.costMaster.getRelationOperator() == RelationOperator.LargerThen)
628                {
629                    if (vk.value > costList.Last().value)
630                    {
631                        node = costList.First;
632                        while (node != null)
633                        {
634                            if (vk.value > node.Value.value)
635                            {
636                                costList.AddBefore(node, vk);
637                                costList.RemoveLast();
638                                value_threshold = costList.Last.Value.value;
639                                break;
640                            }
641                            node = node.Next;
642                        }//end while
643                    }//end if
644                }
645                else
646                {
647                    if (vk.value < costList.Last().value)
648                    {
649                        node = costList.First;
650                        while (node != null)
651                        {
652                            if (vk.value < node.Value.value)
653                            {
654                                costList.AddBefore(node, vk);
655                                costList.RemoveLast();
656                                value_threshold = costList.Last.Value.value;
657                                break;
658                            }
659                            node = node.Next;
660                        }//end while
661                    }//end if
662                }
663            }
664        }
665
666        #endregion
667
668        private void startThreads(IControlEncryption sender, int bytesToUse, KeyPattern[] patterns, BigInteger[] doneKeysA, BigInteger[] keycounters, BigInteger[] keysleft, Stack threadStack)
669        {
670            for (int i = 0; i < patterns.Length; i++)
671            {
672                WaitCallback worker = new WaitCallback(KeySearcherJob);
673                doneKeysA[i] = new BigInteger();
674                keycounters[i] = new BigInteger();
675                ThreadPool.QueueUserWorkItem(worker, new object[] { patterns, i, doneKeysA, keycounters, keysleft, sender.clone(), bytesToUse, threadStack });
676            }
677        }
678
679        private KeyPattern[] splitPatternForThreads(KeyPattern pattern)
680        {
681            KeyPattern[] patterns = new KeyPattern[settings.CoresUsed + 1];
682            if (settings.CoresUsed > 0)
683            {
684                KeyPattern[] patterns2 = pattern.split();
685                patterns[0] = patterns2[0];
686                patterns[1] = patterns2[1];
687                int p = 1;
688                int threads = settings.CoresUsed - 1;
689                while (threads > 0)
690                {
691                    int maxPattern = -1;
692                    BigInteger max = 0;
693                    for (int i = 0; i <= p; i++)
694                        if (patterns[i].size() > max)
695                        {
696                            max = patterns[i].size();
697                            maxPattern = i;
698                        }
699                    KeyPattern[] patterns3 = patterns[maxPattern].split();
700                    patterns[maxPattern] = patterns3[0];
701                    patterns[++p] = patterns3[1];
702                    threads--;
703                }
704            }
705            else
706                patterns[0] = Pattern;
707            return patterns;
708        }
709
710        private void keyPatternChanged()
711        {
712            Pattern = new KeyPattern(controlMaster.getKeyPattern());
713        }
714
715        // set to protected by Christian Arnold - 2009.12.06
716        protected virtual void onStatusChanged(IControl sender, bool readyForExecution)
717        {
718            if (readyForExecution)
719            {
720                this.process((IControlEncryption)sender);
721            }
722        }
723
724
725        // added by Arnie - 2009.12.07
726        public delegate void BruteforcingEnded(LinkedList<ValueKey> top10List);
727        /// <summary>
728        /// This event gets thrown after Bruteforcing had ended. This is no evidence, that bruteforcing was successful.
729        /// But when the returned List is filled, we have (at least a part) of the possible best keys
730        /// </summary>
731        public event BruteforcingEnded OnBruteforcingEnded;
732
733        // added by Arnie -2009.12.02
734        // for inheritance reasons
735        public void BruteforcePattern(KeyPattern pattern, IControlEncryption encryptControl, IControlCost costControl)
736        {
737            //ControlMaster = encryptControl;
738            //CostMaster = costControl;
739            LinkedList<ValueKey> lstRet = bruteforcePattern(pattern, encryptControl);
740            if(OnBruteforcingEnded != null)
741                OnBruteforcingEnded(lstRet);
742        }
743
744        #endregion
745
746        public void GuiLogMessage(string message, NotificationLevel loglevel)
747        {
748            if (OnGuiLogNotificationOccured != null)
749                OnGuiLogNotificationOccured(this, new GuiLogEventArgs(message, this, loglevel));
750        }
751
752        public void ProgressChanged(double value, double max)
753        {
754            if (OnPluginProgressChanged != null)
755            {
756                OnPluginProgressChanged(this, new PluginProgressEventArgs(value, max));
757
758            }
759        }
760
761        /// <summary>
762        /// used for delivering the results from the worker threads to the main thread:
763        /// </summary>
764        public struct ValueKey
765        {
766            public double value;
767            public String key;
768            public byte[] decryption;
769        };
770    }
771
772    /// <summary>
773    /// Represents one entry in our result list
774    /// </summary>
775    public class ResultEntry
776    {
777        public string Ranking { get; set; }
778        public string Value { get; set; }
779        public string Key { get; set; }
780        public string Text { get; set; }
781
782    }
783}
Note: See TracBrowser for help on using the repository browser.