Merge pull request #225 from mistoll/master
[mono.git] / mcs / class / corlib / Test / System.Security.Cryptography / RandomNumberGeneratorTest.cs
1 //
2 // RandomNumberGeneratorTest.cs - NUnit Test Cases for RNG
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // (C) 2002 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
9 //
10
11 using NUnit.Framework;
12 using System;
13 using System.IO;
14 using System.Security.Cryptography;
15 using System.Text;
16
17 namespace MonoTests.System.Security.Cryptography {
18
19 // References:
20 // a.   NIST FIPS PUB 140-2: Security requirements for Cryptographic Modules 
21 //      http://csrc.nist.gov/publications/fips/fips140-2/fips1402.pdf
22 // b.   NIST SP 800-22: A Statistical Test Suite for Random and Pseudorandom Number Generators for Cryptographic Applications
23 //      not implemented
24 //      http://csrc.nist.gov/publications/nistpubs/800-22/sp-800-22-051501.pdf
25 // c.   IETF RFC1750: Randomness Recommendations for Security
26 //      not implemented
27 //      http://www.ietf.org/rfc/rfc1750.txt
28
29 [TestFixture]
30 public class RandomNumberGeneratorTest {
31
32         private string name;
33         private byte[] sample;
34
35         [TestFixtureSetUp]
36         public void SetUp () 
37         {
38                 // all tests should be done on the same random sample
39                 RandomNumberGenerator rng = RandomNumberGenerator.Create ();
40                 name = rng.ToString ();
41                 // 20,000 bits
42                 sample = new byte[2500];
43                 rng.GetBytes (sample);
44         }
45
46         // count the number of 1
47         [Test]
48         public void Monobit () 
49         {
50                 int x = 0;
51                 for (int i=0; i < sample.Length; i++) {
52                         byte b = sample[i];
53                         for (int j = 0; j < 8; j++) {
54                                 if ((b & 0x01) == 0x01)
55                                         x++;
56                                 // next bit
57                                 b >>= 1;
58                         }
59                 }
60                 Assert.IsTrue ((9725  < x), String.Format ("{0} Monobit x={1} > 9725",  name, x));
61                 Assert.IsTrue ((x < 10275), String.Format ("{0} Monobit x={1} < 10275", name, x));
62         }
63
64         // 16 patterns (nibbles)
65         [Test]
66         public void Poker () 
67         {
68                 int[] pattern = new int[16];
69                 for (int i = 0; i < sample.Length; i++) {
70                         byte b = sample[i];
71                         int n = (b & 0x0F);
72                         pattern[n]++;
73                         b >>= 4;
74                         n = b;
75                         pattern[n]++;
76                 }
77                 double result = 0;
78                 for (int i = 0; i < 16; i++)
79                         result += (pattern[i] * pattern[i]);
80                 result = ((16 * result) / 5000) - 5000;
81
82                 Assert.IsTrue (((result > 2.16) && (result < 46.17)), name + " Poker: " + result);
83         }
84
85         // runs of 1 (or 0)
86         [Test]
87         public void Runs () 
88         {
89                 int[,] runs = new int[6,2];
90                 int x = 0;
91                 bool one = false;
92                 bool zero = false;
93                 for (int i = sample.Length - 1; i >= 0 ; i--) {
94                         byte b = sample[i];
95                         for (int j = 0; j < 8; j++) {
96                                 if ((b & 0x01) == 0x01) {
97                                         if (!one) {
98                                                 one = true;
99                                                 zero = false;
100                                                 int p = Math.Min (x, 6) - 1;
101                                                 if (p >= 0)
102                                                         runs[p,0]++;
103                                                 x = 0;
104                                         }
105                                 }
106                                 else {
107                                         if (!zero) {
108                                                 one = false;
109                                                 zero = true;
110                                                 int p = Math.Min (x, 6) - 1;
111                                                 if (p >= 0)
112                                                         runs[p,1]++;
113                                                 x = 0;
114                                         }
115                                 }
116                                 x++;
117                                 // next bit
118                                 b >>= 1;
119                         }
120                 }
121                 // don't forget the ast run
122                 if (x > 0) {
123                         int p = Math.Min (x, 6) - 1;
124                         if (p >= 0)
125                                 runs [p, zero ? 0 : 1]++;
126                 }
127                 // Updated ranges as per FIPS140-2 Change Notice #1
128                 // check for runs of zeros
129                 Assert.IsTrue (((runs[0,0] >= 2315) && (runs[0,0] <= 2685)), name + " 0-Runs length=1: " + runs[0,0]);
130                 Assert.IsTrue (((runs[1,0] >= 1114) && (runs[1,0] <= 1386)), name + " 0-Runs length=2: " + runs[1,0]);
131                 Assert.IsTrue (((runs[2,0] >=  527) && (runs[2,0] <=  723)), name + " 0-Runs length=3: " + runs[2,0]);
132                 Assert.IsTrue (((runs[3,0] >=  240) && (runs[3,0] <=  384)), name + " 0-Runs length=4: " + runs[3,0]);
133                 Assert.IsTrue (((runs[4,0] >=  103) && (runs[4,0] <=  209)), name + " 0-Runs length=5: " + runs[4,0]);
134                 Assert.IsTrue (((runs[5,0] >=  103) && (runs[5,0] <=  209)), name + " 0-Runs length=6+ " + runs[5,0]);
135                 // check for runs of ones
136                 Assert.IsTrue (((runs[0,1] >= 2315) && (runs[0,1] <= 2685)), name + " 1-Runs length=1: " + runs[0,1]);
137                 Assert.IsTrue (((runs[1,1] >= 1114) && (runs[1,1] <= 1386)), name + " 1-Runs length=2: " + runs[1,1]);
138                 Assert.IsTrue (((runs[2,1] >=  527) && (runs[2,1] <=  723)), name + " 1-Runs length=3: " + runs[2,1]);
139                 Assert.IsTrue (((runs[3,1] >=  240) && (runs[3,1] <=  384)), name + " 1-Runs length=4: " + runs[3,1]);
140                 Assert.IsTrue (((runs[4,1] >=  103) && (runs[4,1] <=  209)), name + " 1-Runs length=5: " + runs[4,1]);
141                 Assert.IsTrue (((runs[5,1] >=  103) && (runs[5,1] <=  209)), name + " 1-Runs length=6+ " + runs[5,1]);
142         }
143
144         // no long runs of 26 or more (0 or 1)
145         [Test]
146         public void LongRuns () 
147         {
148                 int longestRun = 0;
149                 int currentRun = 0;
150                 bool one = false;
151                 bool zero = false;
152                 for (int i = sample.Length - 1; i >= 0 ; i--) {
153                         byte b = sample[i];
154                         for (int j = 0; j < 8; j++) {
155                                 if ((b & 0x01) == 0x01) {
156                                         if (!one) {
157                                                 one = true;
158                                                 zero = false;
159                                                 longestRun = Math.Max (longestRun, currentRun);
160                                                 currentRun = 0;
161                                         }
162                                         currentRun++;
163                                 }
164                                 else {
165                                         if (!zero) {
166                                                 one = false;
167                                                 zero = true;
168                                                 longestRun = Math.Max (longestRun, currentRun);
169                                                 currentRun = 0;
170                                         }
171                                         currentRun++;
172                                 }
173                                 // next bit
174                                 b >>= 1;
175                         }
176                 }
177                 Assert.IsTrue ((longestRun < 26), name + " Long Runs max = " + longestRun);
178         }
179 }
180
181 }