/*
* Portions Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*/
/* --------------------------------------------------------------------------
Title : The NIST Statistical Test Suite
Date : December 1999
Programmer : Juan Soto
Summary : For use in the evaluation of the randomness of bitstreams
produced by cryptographic random number generators.
Package : Version 1.0
Copyright : (c) 1999 by the National Institute Of Standards & Technology
History : Version 1.0 by J. Soto, October 1999
Revised by J. Soto, November 1999
Revised by Larry Bassham, March 2008
Keywords : Pseudorandom Number Generator (PRNG), Randomness, Statistical
Tests, Complementary Error functions, Incomplete Gamma
Function, Random Walks, Rank, Fast Fourier Transform,
Template, Cryptographically Secure PRNG (CSPRNG),
Approximate Entropy (ApEn), Secure Hash Algorithm (SHA-1),
Blum-Blum-Shub (BBS) CSPRNG, Micali-Schnorr (MS) CSPRNG,
Source : David Banks, Elaine Barker, James Dray, Allen Heckert,
Stefan Leigh, Mark Levenson, James Nechvatal, Andrew Rukhin,
Miles Smid, Juan Soto, Mark Vangel, and San Vo.
Technical
Assistance : Larry Bassham, Ron Boisvert, James Filliben, Daniel Lozier,
and Bert Rust.
Warning : Portability Issues.
Limitation : Amount of memory allocated for workspace.
Restrictions: Permission to use, copy, and modify this software without
fee is hereby granted, provided that this entire notice is
included in all copies of any software which is or includes
a copy or modification of this software and in all copies
of the supporting documentation for such software.
-------------------------------------------------------------------------- */
//system include
#include <e32base.h>
#include <e32std.h>
#include <e32cons.h>
// user include
#include "openc.h"
#include "../include/decls.h"
#include "../include/cephes.h"
#include "../include/utilities.h"
#include "../include/generators.h"
typedef int (*CompareFnType)(const void*, const void*);
void partitionResultFile(int numOfFiles, int numOfSequences, int option, int testNameID);
void postProcessResults(int option);
int cmp(const double *a, const double *b);
int computeMetrics(char *s, int test);
int
StartNISTTest()
{
int i;
int option = 10; /* TEMPLATE LENGTH/STREAM LENGTH/GENERATOR*/
char *streamFile = NULL; /* STREAM FILENAME */
tp.n = 1000000; // length of the individual bit stream(s) to be processed
tp.blockFrequencyBlockLength = 128;
tp.nonOverlappingTemplateBlockLength = 9;
tp.overlappingTemplateBlockLength = 9;
tp.approximateEntropyBlockLength = 10;
tp.serialBlockLength = 16;
tp.linearComplexitySequenceLength = 500;
tp.numOfBitStreams = 100;
chooseTests();
fixParameters();
openOutputStreams(option);
invokeTestSuite(option, streamFile);
fclose(freqfp);
for( i=1; i<=NUMOFTESTS; i++ ) {
if ( stats[i] != NULL )
fclose(stats[i]);
if ( results[i] != NULL )
fclose(results[i]);
}
if ( (testVector[0] == 1) || (testVector[TEST_CUSUM] == 1) )
partitionResultFile(2, tp.numOfBitStreams, option, TEST_CUSUM);
if ( (testVector[0] == 1) || (testVector[TEST_NONPERIODIC] == 1) )
partitionResultFile(MAXNUMOFTEMPLATES, tp.numOfBitStreams, option, TEST_NONPERIODIC);
if ( (testVector[0] == 1) || (testVector[TEST_RND_EXCURSION] == 1) )
partitionResultFile(8, tp.numOfBitStreams, option, TEST_RND_EXCURSION);
if ( (testVector[0] == 1) || (testVector[TEST_RND_EXCURSION_VAR] == 1) )
partitionResultFile(18, tp.numOfBitStreams, option, TEST_RND_EXCURSION_VAR);
if ( (testVector[0] == 1) || (testVector[TEST_SERIAL] == 1) )
partitionResultFile(2, tp.numOfBitStreams, option, TEST_SERIAL);
fprintf(summary, "------------------------------------------------------------------------------\n");
fprintf(summary, "RESULTS FOR THE UNIFORMITY OF P-VALUES AND THE PROPORTION OF PASSING SEQUENCES\n");
fprintf(summary, "------------------------------------------------------------------------------\n");
fprintf(summary, " C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 P-VALUE PROPORTION STATISTICAL TEST\n");
fprintf(summary, "------------------------------------------------------------------------------\n");
postProcessResults(option);
fclose(summary);
return 1;
}
void
partitionResultFile(int numOfFiles, int numOfSequences, int option, int testNameID)
{
int i, k, m, j, start, end, num, numread;
float c;
FILE **fp = (FILE **)calloc(numOfFiles+1, sizeof(FILE *));
int *results = (int *)calloc(numOfFiles, sizeof(int *));
char *s[MAXFILESPERMITTEDFORPARTITION];
char resultsDir[200];
for ( i=0; i<MAXFILESPERMITTEDFORPARTITION; i++ )
s[i] = (char*)calloc(200, sizeof(char));
sprintf(resultsDir, "%s\\experiments\\%s\\%s\\results", gLogFilePath.PtrZ(), generatorDir[option], testNames[testNameID]);
if ( (fp[numOfFiles] = fopen(resultsDir, "r")) == NULL ) {
printf("%s", resultsDir);
printf(" -- file not found. Exiting program.\n");
exit(-1);
}
for ( i=0; i<numOfFiles; i++ )
{
sprintf(s[i], "%s\\experiments\\%s\\%s\\data%d", gLogFilePath.PtrZ(), generatorDir[option], testNames[testNameID], i+1);
}
numread = 0;
m = numOfFiles/20;
if ( (numOfFiles%20) != 0 )
m++;
for ( i=0; i<numOfFiles; i++ ) {
if ( (fp[i] = fopen(s[i], "w")) == NULL ) {
printf("%s", s[i]);
printf(" -- file not found. Exiting program.\n");
exit(-1);
}
fclose(fp[i]);
}
for ( num=0; num<numOfSequences; num++ ) {
for ( k=0; k<m; k++ ) { /* FOR EACH BATCH */
start = k*20; /* BOUNDARY SEGMENTS */
end = k*20+19;
if ( k == (m-1) )
end = numOfFiles-1;
for ( i=start; i<=end; i++ ) { /* OPEN FILE */
if ( (fp[i] = fopen(s[i], "a")) == NULL ) {
printf("%s", s[i]);
printf(" -- file not found. Exiting program.\n");
exit(-1);
}
}
for ( j=start; j<=end; j++ ) { /* POPULATE FILE */
fscanf(fp[numOfFiles], "%f", &c);
fprintf(fp[j], "%f\n", c);
numread++;
}
for ( i=start; i<=end; i++ ) /* CLOSE FILE */
fclose(fp[i]);
}
}
fclose(fp[numOfFiles]);
for ( i=0; i<MAXFILESPERMITTEDFORPARTITION; i++ )
free(s[i]);
free(fp);
free(results);
return;
}
int
cmp(const double *a, const double *b)
{
if ( *a < *b )
return -1;
if ( *a == *b )
return 0;
return 1;
}
void
postProcessResults(int option)
{
int i, k;
int randomExcursionSampleSize = 0;
int generalSampleSize = 0;
int case1, case2, numOfFiles = 2;
double passRate;
char s[200];
for ( i=1; i<=NUMOFTESTS; i++ ) { // FOR EACH TEST
if ( testVector[i] ) {
// SPECIAL CASES -- HANDLING MULTIPLE FILES FOR A SINGLE TEST
if ( ((i == TEST_CUSUM) && testVector[TEST_CUSUM] ) ||
((i == TEST_NONPERIODIC) && testVector[TEST_NONPERIODIC] ) ||
((i == TEST_RND_EXCURSION) && testVector[TEST_RND_EXCURSION]) ||
((i == TEST_RND_EXCURSION_VAR) && testVector[TEST_RND_EXCURSION_VAR]) ||
((i == TEST_SERIAL) && testVector[TEST_SERIAL]) ) {
if ( (i == TEST_NONPERIODIC) && testVector[TEST_NONPERIODIC] )
numOfFiles = MAXNUMOFTEMPLATES;
else if ( (i == TEST_RND_EXCURSION) && testVector[TEST_RND_EXCURSION] )
numOfFiles = 8;
else if ( (i == TEST_RND_EXCURSION_VAR) && testVector[TEST_RND_EXCURSION_VAR] )
numOfFiles = 18;
else
numOfFiles = 2;
for ( k=0; k<numOfFiles; k++ ) {
if ( k < 10 )
sprintf(s, "%s\\experiments\\%s\\%s\\data%1d", gLogFilePath.PtrZ(), generatorDir[option], testNames[i], k+1);
else if ( k < 100 )
sprintf(s, "%s\\experiments\\%s\\%s\\data%2d", gLogFilePath.PtrZ(), generatorDir[option], testNames[i], k+1);
else
sprintf(s, "%s\\experiments\\%s\\%s\\data%3d", gLogFilePath.PtrZ(), generatorDir[option], testNames[i], k+1);
if ( (i == TEST_RND_EXCURSION) || (i == TEST_RND_EXCURSION_VAR) )
randomExcursionSampleSize = computeMetrics(s,i);
else
generalSampleSize = computeMetrics(s,i);
}
}
else {
sprintf(s, "%s\\experiments\\%s\\%s\\results", gLogFilePath.PtrZ(), generatorDir[option], testNames[i]);
generalSampleSize = computeMetrics(s,i);
}
}
}
fprintf(summary, "\n\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n");
case1 = 0;
case2 = 0;
if ( testVector[TEST_RND_EXCURSION] || testVector[TEST_RND_EXCURSION_VAR] )
case2 = 1;
for ( i=1; i<=NUMOFTESTS; i++ ) {
if ( testVector[i] && (i != TEST_RND_EXCURSION) && (i != TEST_RND_EXCURSION_VAR) ) {
case1 = 1;
break;
}
}
if ( case1 ) {
if ( generalSampleSize == 0 ) {
fprintf(summary, "The minimum pass rate for each statistical test with the exception of the\n");
fprintf(summary, "random excursion (variant) test is undefined.\n\n");
}
else {
passRate = 0.99-3.0*sqrt(0.01*(1.0-ALPHA)/(double)generalSampleSize);
fprintf(summary, "The minimum pass rate for each statistical test with the exception of the\n");
fprintf(summary, "random excursion (variant) test is approximately = %f for a\n", generalSampleSize ? passRate : 0.0);
fprintf(summary, "sample size = %d binary sequences.\n\n", generalSampleSize);
}
}
if ( case2 ) {
if ( randomExcursionSampleSize == 0 )
fprintf(summary, "The minimum pass rate for the random excursion (variant) test is undefined.\n\n");
else {
passRate = 0.99-3.0*sqrt(0.01*(1.0-ALPHA)/(double)randomExcursionSampleSize);
fprintf(summary, "The minimum pass rate for the random excursion (variant) test\n");
fprintf(summary, "is approximately %f for a sample size = %d binary sequences.\n\n", passRate, randomExcursionSampleSize);
}
}
fprintf(summary, "For further guidelines construct a probability table using the MAPLE program\n");
fprintf(summary, "provided in the addendum section of the documentation.\n");
fprintf(summary, "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n");
}
int
computeMetrics(char *s, int test)
{
int j, pos, count, sampleSize, expCount;
int freqPerBin[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
double *A = NULL;
double *T = NULL;
double chi2, proportion, uniformity;
float c;
FILE *fp = NULL;
if ( (fp = fopen(s, "r")) == NULL ) {
printf("%s",s);
printf(" -- file not found. Exiting program.\n");
exit(-1);
}
/* Compute Metric 1: Proportion of Passing Sequences */
count = 0;
sampleSize = tp.numOfBitStreams;
if ( (test == TEST_RND_EXCURSION) || (test == TEST_RND_EXCURSION_VAR) ) { /* Special Case: Random Excursion Tests */
if ( (T = (double *)calloc(tp.numOfBitStreams, sizeof(double))) == NULL ) {
printf("Final Analysis Report aborted due to insufficient workspace\n");
// Perform cleanup before returning
fclose(fp);
return 0;
}
for ( j=0; j<sampleSize; j++ ) {
fscanf(fp, "%f", &c);
if ( c > 0.000000 )
T[count++] = c;
}
if ( (A = (double *)calloc(count, sizeof(double))) == NULL ) {
printf("Final Analysis Report aborted due to insufficient workspace\n");
// Perform cleanup before returning
fclose(fp);
free(T);
return 0;
}
for ( j=0; j<count; j++ )
A[j] = T[j];
sampleSize = count;
count = 0;
for ( j=0; j<sampleSize; j++ )
if ( A[j] < ALPHA )
count++;
free(T);
T = NULL;
}
else {
if ( (A = (double *)calloc(sampleSize, sizeof(double))) == NULL ) {
printf("Final Analysis Report aborted due to insufficient workspace\n");
// Perform cleanup before returning
fclose(fp);
return 0;
}
for ( j=0; j<sampleSize; j++ ) {
fscanf(fp, "%f", &c);
if ( c < ALPHA )
count++;
A[j] = c;
}
}
if ( sampleSize == 0 )
proportion = 0.0;
else
proportion = 1.0-(double)count/sampleSize;
/* Compute Metric 2: Histogram */
qsort((void *)A, sampleSize, sizeof(double), (CompareFnType)cmp);
for ( j=0; j<sampleSize; j++ ) {
pos = (int)floor(A[j]*10);
if ( pos == 10 )
pos--;
freqPerBin[pos]++;
}
chi2 = 0.0;
expCount = sampleSize/10;
for ( j=0; j<10; j++ )
chi2 += pow(freqPerBin[j]-expCount, 2)/expCount;
uniformity = cephes_igamc(9.0/2.0, chi2/2.0);
for ( j=0; j<10; j++ ) /* DISPLAY RESULTS */
fprintf(summary, "%3d ", freqPerBin[j]);
if ( expCount == 0 )
fprintf(summary, " ---- ");
else if ( uniformity < 0.0001 )
fprintf(summary, " %8.6f * ", uniformity);
else
fprintf(summary, " %8.6f ", uniformity);
if ( sampleSize == 0 )
fprintf(summary, " ---- %s\n", testNames[test]);
else if ( proportion < 0.96 )
fprintf(summary, "%6.4f * %s\n", proportion, testNames[test]);
else
fprintf(summary, "%6.4f %s\n", proportion, testNames[test]);
fclose(fp);
free(A);
return sampleSize;
}
/*
Gobal Entry Function
*/
GLDEF_C TInt E32Main()
{
__UHEAP_MARK;
TInt ret = StartNISTTest();
__UHEAP_MARKEND;
return ret;
}