Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / security / encryption

Getting a strong random digit using RNGCryptoServiceProvider

4.00/5 (3 votes)
15 May 2013CPOL1 min read 29.4K  
Generating a random number between 0 and 9.

Introduction

I tried to write code that would return an integer of value between 0 and 9 without losing the randomness strength provided in the class RandomNumberGenerator (RNGCryptoServiceProvider) in the System.Security.Cryptography namespace. The hard part was that the byte array is filled with random bytes valued between 0 and 255.

Basic idea and method

If we use only integers 0, 1, 2, 3 and we randomly pick one three times from this collection, the sum of the three draws wil always have a value between 0 and 9.

If a byte has a value between 0 and 255 we also can say that this byte is an even or odd number. The even and odd numbers are equally represented in a set of (0-255).

Therefore if we take two bytes the possibilities of the outcome for a byte pair (b1 and b2) will always be:

  1. b(1)odd b(0)even = 0
  2. b(1)odd b(1)odd = 1
  3. b(0)even b(0)even = 2
  4. b(1)even b(2)odd = 3

If we populate a byte array with random 6 bytes (3 pairs) and compare 3x two bytes, we will have 3x3 possible values between 0 and 3.

For example: Array X= {a,b,c,d,e,f}

  • Bytes: [b1,b2] - [b3,b4] - [b5,b6]
  • Valued: [11][34] -[55][47] - [55][66]
  • Are: odd/even -odd/odd -odd/even
  • Resulting in: 3 + 3 + 2 = 8

(After each round I reverse the odd/even values for better distribution. This was also done in the Hot Bits Radiation project. After testing the Bell curve was better distributed.)

Using the code

There is a class file RandomDigits and a Console application.

The class file:

C#
//M.C. Koutzarov Class file RandomDigits [0-9]

using System;
using System.Security.Cryptography;
using System.Text;

namespace ps.security{
    public class RandomDigits
    {     
        ///<summary>
        /// Returns One random Digit of a value between 0 and 9.
        ///</summary>   
        public static int getDigit(){
            int[] Result = { 0, 0, 0 }; // this stores sum1 , sum2 and sum3 
            int R = 0; // Will hold the resulting integer Sum of Result{3}
            int b1 = 0; // byte one
            int b2 = 0; // byte two 
            int offset = 0;
            // need an offset to loop 3 times. Each time i get 2 bytes to compare.
          
            //get 6 random bytes
            RandomNumberGenerator rng = new RNGCryptoServiceProvider();
            
            byte[] b = new byte[6];
             
            rng.GetNonZeroBytes(b);


            for (int i =0; i <=2; i++)  // Loop 3 time  i=2 i=1 and i=0
            { 
                //get the offset 
                if (i == 2) {
                    offset = 4; // 5th and 6th byte 
                }else if(i == 1) {
                    offset = 2; // 3th and 4th byte 
                }else if (i == 0){
                    offset = 0; // 1th and 2th byte
                };

                //(b1,2 MOD 2) b1 and b2 will be odd number is 1 and even if 0
                b1 = ((int)b[offset] % 2);
                b2 = ((int)b[offset+1] % 2);


                if ((i == 0))
                {
                    if ((b1 == 1) && (b2 == 1))
                    {
                        Result[i] = 0;              //odd-odd   = 0
                    }
                    else if ((b1 == 0) && (b2 == 1))
                    {
                        Result[i] = 1;              //even-odd  = 1
                    }
                    else if ((b1 == 0) && (b2 == 0))
                    {
                        Result[i] = 2;              //even-even = 2  
                    }
                    else if ((b1 == 1) && (b2 == 0))
                    {
                        Result[i] = 3;
                    };           //odd-even  = 3
                }
                else if (i==1) {
                    // on second round flip the values 
                    if ((b1 == 1) && (b2 == 1))
                    {
                        Result[i] = 3;              //odd-odd   = 3
                    }
                    else if ((b1 == 0) && (b2 == 1))
                    {
                        Result[i] = 2;              //even-odd  = 2
                    }
                    else if ((b1 == 0) && (b2 == 0))
                    {
                        Result[i] = 1;              //even-even = 1  
                    }
                    else if ((b1 == 1) && (b2 == 0))
                    {
                        Result[i] = 0;          //odd-even  = 0
                    };

                }
                else if (i == 2) {

                    // on 3 round  flip the values once more
                    if ((b1 == 1) && (b2 == 1))
                    {
                        Result[i] = 1;              //odd-odd   = 1
                    }
                    else if ((b1 == 0) && (b2 == 1))
                    {
                        Result[i] = 3;              //even-odd  = 3
                    }
                    else if ((b1 == 0) && (b2 == 0))
                    {
                        Result[i] = 0;              //even-even = 0  
                    }
                    else if ((b1 == 1) && (b2 == 0))
                    {
                        Result[i] = 2;          //odd-even  = 2
                    };
                }

                b1 = 0;
                b2 = 0;

            };// end 3 for loops

            //Sum the results of round 1,2 and 3 together
            //this will make an Int between 0 - 9 
            R =(int)(Result[0] + Result[1] + Result[2]);

            //Return a Int valued between 0 - 9
            return R;
        }      
    }
}

Here is the console testing code:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ps.security;
using System.Diagnostics; 

//M.C. Koutzarov Testing Randomness for digits [0-9]

    class Program
    {
        static void Main(string[] args){
             Program.DrawNumbersPerDigitTest();
             Console.ReadKey();
             Console.WriteLine("");
             Console.WriteLine("Test 2");
             Program.DrawDigitTest(1000000); 
        }
   
        // Draws a the digits 0-9 
        // each digit(from 0-9) is being drawn untill it appears
        // At the end it shows the Total draws needed for all didigts to appear.
        // and the avg number of draws per digit.
        public static int[] DrawNumbersPerDigitTest()
        {
            Console.WriteLine("Random digits  0-9 generator test.");
            double frequency = System.Diagnostics.Stopwatch.Frequency;
            double nanosecPerTick = (1000 * 1000 * 1000) / frequency;
            int totalItter = 0;
            int[] Totaldistri = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
            for (int AllDigits = 9; AllDigits >= 0; AllDigits--)
            {
                Stopwatch timer = new Stopwatch();
                int avg = 0;
                int itters = 0;
                int[] distri = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
                timer.Start();
                for (int i = 1000000; i >= 1; i--)
                {
                    int n = ps.security.RandomDigits.getDigit();
                    avg = (avg + n);
                    distri[n]++;
                    itters++;
                    if (n == AllDigits)
                    {
                        break;
                    }
                };
                timer.Stop();
                
                double durNanosec = timer.ElapsedTicks * nanosecPerTick;
                double durSecs = timer.ElapsedTicks * 1000 / frequency;
                
                Console.WriteLine("");
                Console.WriteLine("|----------------------------------|");
                Console.WriteLine("Digit nr:\t" + AllDigits);
                Console.WriteLine("");
                Console.WriteLine("|  int\t|  freq\t percentage\t   |");
                Console.WriteLine("|----------------------------------|");
                for (int i = 9; i >= 0; i--)
                {
                    double p =Math.Round( ((double)distri[i])/((double)itters / 100),3);
                    Console.WriteLine("|  " + i + "\t|  " + 
                       +distri[i] + "\t " + p + "%\t");
                }
                Console.WriteLine("|----------------------------------|");
                Console.WriteLine("");
                Console.WriteLine("  Sum     : >\t" + avg.ToString());
                Console.WriteLine("  Average : >\t" + 
                        ((double)avg / (double)itters).ToString());
                Console.WriteLine("  Count   : >\t" + (itters).ToString());
                Console.WriteLine("");
                Console.WriteLine("  nSecs   :\t" + Math.Round(durNanosec, 3));
                Console.WriteLine("  mSecs   :\t" + Math.Round(durSecs, 9));
                totalItter += (itters);
                Totaldistri[AllDigits] = itters;
            };
            Console.WriteLine("");
            Console.WriteLine("|----------------------------------|");
            Console.WriteLine("Average + total draws per Digit:");
            Console.WriteLine("|----------------------------------|");
            Console.WriteLine("");
            Console.WriteLine("  total draws\t: >\t" + totalItter.ToString());
            Console.WriteLine("  avg\t\t: >\t" + 
                    Math.Round((double)totalItter / 10, 3));
            Console.WriteLine("|  int\t|  freq\t ");
            Console.WriteLine("|----------------------------------|");
            for (int i = 9; i >= 0; i--)
            {
                Console.WriteLine("|  " + i + "\t|  " + 
                          +Totaldistri[i] + "\t \t");
            }
            return Totaldistri;
     }
     // Draws a the digits 0-9 
     // each digit(from 0-9) is being drawn untill it appears
     // At the end it shows the Total draws needed for all didigts to appear.
     // and the avg number of draws per digit.
     public static void DrawDigitTest(int nrOfDigits)
     {
         Console.WriteLine("Random digits  0-9 generator test.");
         double frequency = System.Diagnostics.Stopwatch.Frequency;
         double nanosecPerTick = (1000 * 1000 * 1000) / frequency;
        
         Stopwatch timer = new Stopwatch();
         int avg = 0;
         int itters = 0;
         int[] distri = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
         timer.Start();
         for (int i = nrOfDigits; i >= 1; i--)
         {
             int n = ps.security.RandomDigits.getDigit();
             avg = (avg + n);
             distri[n]++;
             itters++;
         };
         timer.Stop();
         double durNanosec = timer.ElapsedTicks * nanosecPerTick;
         double durSecs = timer.ElapsedTicks * 1000 / frequency;
        
         Console.WriteLine("");
         Console.WriteLine("| int\t|  freq\t percentage\t   |");
         Console.WriteLine("|----------------------------------|");
         for (int i = 9; i >= 0; i--)
         {
             double p = Math.Round(((double)distri[i]) / ((double)itters / 100), 1);
             Console.WriteLine("| " + i + "\t| " + 
               +distri[i] + "\t\t" + p + "%.");
         }
         Console.WriteLine("|----------------------------------|");
         Console.WriteLine("");
         Console.WriteLine("  #Digits : >\t\t" + (itters).ToString());
         Console.WriteLine("  Sum     : >\t\t" + avg.ToString());
         Console.WriteLine("  Average : >\t\t" + 
               ((double)avg / (double)itters).ToString());
         
         Console.WriteLine("");
         Console.WriteLine("  nSecs   : >\t" + Math.Round(durNanosec, 3));
         Console.WriteLine("  mSecs   : >\t" + Math.Round(durSecs, 9));
     }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)