summary refs log tree commit diff
path: root/crypto/src/crypto/prng/ThreadedSeedGenerator.cs
blob: 0a38e5f5a56cd8c7b99945d8b0753ad76f4d8e83 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
using System;
using System.Threading;

#if NO_THREADS
using System.Threading.Tasks;
#endif

namespace Org.BouncyCastle.Crypto.Prng
{
    /**
     * A thread based seed generator - one source of randomness.
     * <p>
     * Based on an idea from Marcus Lippert.
     * </p>
     */
    public class ThreadedSeedGenerator
    {
        private class SeedGenerator
        {
#if NETCF_1_0
			// No volatile keyword, but all fields implicitly volatile anyway
			private int		counter = 0;
			private bool	stop = false;
#else
            private volatile int counter = 0;
            private volatile bool stop = false;
#endif

            private void Run(object ignored)
            {
                while (!this.stop)
                {
                    this.counter++;
                }
            }

            public byte[] GenerateSeed(
                int numBytes,
                bool fast)
            {
#if SILVERLIGHT || PORTABLE
                return DoGenerateSeed(numBytes, fast);
#else
                ThreadPriority originalPriority = Thread.CurrentThread.Priority;
                try
                {
                    Thread.CurrentThread.Priority = ThreadPriority.Normal;
                    return DoGenerateSeed(numBytes, fast);
                }
                finally
                {
                    Thread.CurrentThread.Priority = originalPriority;
                }
#endif
            }

            private byte[] DoGenerateSeed(
                int numBytes,
                bool fast)
            {
                this.counter = 0;
                this.stop = false;

                byte[] result = new byte[numBytes];
                int last = 0;
                int end = fast ? numBytes : numBytes * 8;

#if NO_THREADS
                Task.Factory.StartNew(() => Run(null), TaskCreationOptions.None);
#else
                ThreadPool.QueueUserWorkItem(new WaitCallback(Run));
#endif

                for (int i = 0; i < end; i++)
                {
                    while (this.counter == last)
                    {
                        try
                        {
#if PORTABLE
                            new AutoResetEvent(false).WaitOne(1);
#else
                            Thread.Sleep(1);
#endif
                        }
                        catch (Exception)
                        {
                            // ignore
                        }
                    }

                    last = this.counter;

                    if (fast)
                    {
                        result[i] = (byte)last;
                    }
                    else
                    {
                        int bytepos = i / 8;
                        result[bytepos] = (byte)((result[bytepos] << 1) | (last & 1));
                    }
                }

                this.stop = true;

                return result;
            }
        }

        /**
         * Generate seed bytes. Set fast to false for best quality.
         * <p>
         * If fast is set to true, the code should be round about 8 times faster when
         * generating a long sequence of random bytes. 20 bytes of random values using
         * the fast mode take less than half a second on a Nokia e70. If fast is set to false,
         * it takes round about 2500 ms.
         * </p>
         * @param numBytes the number of bytes to generate
         * @param fast true if fast mode should be used
         */
        public byte[] GenerateSeed(
            int numBytes,
            bool fast)
        {
            return new SeedGenerator().GenerateSeed(numBytes, fast);
        }
    }
}