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]
SetUp()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]
Monobit()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]
Poker()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]
Runs()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]
LongRuns()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 }
182