// fips140-1.c : Die statistischen Tests nach FIPS PUB 140-1. Es sind dieselben Tests wie bei // FIPS PUB 140-2, aber mit weiteren Grenzen. // Es werden hier bewusst weitere Grenzen als in 140-2 verwendet, denn z. B. der Beweiss für die Unangreifbarkeit von OTP // setzt GLEICHVERTEILTE Zufallszahlen voraus und diese Voraussetzung ist NICHT erfüllt, wenn durch enge Test- // Grenzen ein statistisch signifikanter Anteil von Zufallszahlen nicht für OTP verwendet wird, also signifikant keine gleichverteilten // Zufallszahlen verwendet werden. Zudem wird von dem Entwurf zu AIS 31 (www.bsi.de) eine Mindest-Fehlerwahrscheinlichkeit gefordert, // die hier schon jeder der Tests hat u. auch nach dem E. von AIS 31 sollte diese Wahrscheinlichkeit nicht groesser sein. // Weil bei gleichbleibenden Grenzen die Signifikanz mit zunehmender Schlüssellänge zunimmt, wären bei grossen Dateien eher weitere // als engere Grenzen für die Tests empfehlenswert, denn es gibt Angriffs-Verfahren, die darauf basieren, das einige Zufallszahlen // nicht oder nur selten in der Schlüsseldatei enthalten sind! // // Rolf Freitag 6/2001-... /* Additionaly the are some more tests, entropy test and autocorrelation tests, wich do no influence the return code but the logging (to stdout). Because of the few data the tests can find only big statistical conspicuitys. This Program is free software; you can redistribute it and/of modify it under the terms of the GNU public License (GPL). Copyright rolf.freitag at email.de */ // simple version number #define SOFTWARE_VERSION_NUMBER 0.5 /*--------------------------------includes----------------------*/ #include #include #include #include #include #include #include #include #include // __u16 and such static const __u8 mask[8] = // bit masks { (__u8) 128, (__u8) 64, (__u8) 32, (__u8) 16, (__u8) 8, (__u8) 4, (__u8) 2, (__u8) 1 }; static __u8 readbit (__u8 * shre, long int n); static double entropietest(__u8 *inputbuffer,long int done); static double autokorrelationstest(__u8 *inputbuffer,int done); static int monobittest (__u8 * inputbuffer, int done); static double pokertest (__u8 * inputbuffer, int done); static char runtest (__u8 * inputbuffer, int done); static int verbosemode = 0; static int quietmode = 0; /*--------------------------------procedures----------------------*/ /*----------------------------------------------------------------*/ /* __u8 readbit(__u8 *shre,int n) */ /* Typ: Funktion */ /* Parameter: shre: Zeiger auf Puffer */ /* n: Zahlenwert, der bestimmt welches Bit(!!) */ /* aus dem Puffer gelesen wird */ /* Funktion: Gibt 0/1 aus je nachdem ob das bit gesetzt ist */ /* oder nicht. */ /* */ /* */ /* */ /*----------------------------------------------------------------*/ __u8 readbit (__u8 * shre, long int n) { return ( (shre[n / 8] & mask[n % 8]) / mask[n % 8] ); } int readbyte (__u8 * shre, int n) { return ( shre[n] ); } int readword (__u16 * shre, int n) { return ( shre[n] ); } /*----------------------------------------------------------------*/ /* double entropietest(__u8 *inputbuffer) */ /* Typ: Funktion */ /* Parameter: inputbuffer: Zeiger auf Puffer */ /* */ /* Funktion: Gibt die bitweise Entropie der Bytes zurück */ /* */ /* */ /* */ /* */ /*----------------------------------------------------------------*/ double entropietest(__u8 *inputbuffer,long int done) { int fucklint; long int chars[256]= //enthält die Einzelhäufigkeit der Zeichen im Puffe { 0,0,0, }; long int cnt=0; // ein zähler long int middle=0,offset=0; double ent=0.0,pA=0.0,part=0.0; if (verbosemode==1) { printf("\n\nEntropietest:\n0%%----------------------50%%---------------------100%%\n["); } for (cnt=0;cnt<256;cnt++) chars[cnt]=0; for (cnt=0;cnt0) // vermeidet Log(<=0) { part*=log(pA); part/=log(2.0); } ent+=part; } if (verbosemode==1) { printf("]\nEntropie:%3.8lf (%3.5lf%%)\n",-ent,100.0+ent/8.0*100.0); middle=done/256+1; offset=((int)done/log(done))/256; printf("\n Abweichungen vom Mittelwert von ca. %ld Zeichen (nur zusätzliche Information)\n ! bedeuten, daß der Wert um mehr als |%ld| abweicht (gehört nicht zum Test!) \n",middle,offset); } for (cnt=0;cnt<256;cnt++) if (verbosemode==1) { printf(" %3ld %+03.3lf%% %c ",cnt,( (double)( (chars[cnt]) - (middle) )/(double)middle )*100.0, ((chars[cnt]>middle+offset)||(chars[cnt] (__u8) 0) { no_of_ones++; } } if (bytes % 50 == 0) { if (verbosemode == 1) { printf ("."); cheatlint = fflush (stdout); // (LINT will das) fflush gibt zeichen sofort aus } } } if (verbosemode == 1) printf ("]\nErgebnis : %5d ( %2.5f %%)", no_of_ones, no_of_ones / 200.0); if (no_of_ones < 9654) { if (verbosemode == 1) { printf ("\nEs sind weniger als 9654 Einsen vorhanden\n"); } } if (no_of_ones > 10346) { if (verbosemode == 1) { printf ("\nEs sind mehr als 10346 Einsen vorhanden\n"); } } if (no_of_ones < 9725 || no_of_ones > 10275) { no_of_ones *= -1; } /* falls test nicht bestanden wurde wird negative Anzahl zurückgeliefert*/ return no_of_ones; } /*----------------------------------------------------------------*/ /* double pokertest(__u8 *inputbuffer) */ /* Typ: Funktion */ /* Parameter: inputbuffer: Zeiger auf Puffer */ /* */ /* Funktion: Gibt die einen wert zurück, der eine Aussage über */ /* die Verteilung der nibbles(halbbytes) trift */ /* */ /* ein negativer Wert zeigt an, daß der Betrag */ /* nicht in den testgrenzen lag */ /* */ /* */ /*----------------------------------------------------------------*/ double pokertest (__u8 * inputbuffer, int done) { int cheatlint = 0; // take ignorable return values int nibbles[16] = // häufigkeit der nibbles { 0 }; int bytes = 0, nibblecnt = 0; // zähler double nibblesquaresum = 0, nibblesum = 0; // helferlein double result = 0.0; // rückgabewert if (verbosemode == 1) printf ("\n\nZähle Anzahl der Nibbles (Pokertest):\n0%%----------------------50%%---------------------100%%\n["); for (bytes = 0; bytes < done; bytes++) { nibbles[0x0f & (int) inputbuffer[bytes]]++; //lower nibble; //higher nibble nibbles[(0xf0 & (int) inputbuffer[bytes]) >> 4]++; if (bytes % (done / 50) == 0) { if (verbosemode == 1) printf ("."); cheatlint = fflush (stdout); // (LINT will das) fflush gibt zeichen sofort aus } } if (verbosemode == 1) printf ("]\nErgebnis :%f\n", result); //FEHLER??? result not used for (nibblecnt = 0; nibblecnt < 16; nibblecnt++) { if (verbosemode == 1) printf (" %5d %5d ( %2.5f %%)\n", nibblecnt, nibbles[nibblecnt], nibbles[nibblecnt] / (double) done * 2500.0); nibblesum = (double) nibbles[nibblecnt]; nibblesquaresum += nibblesum * nibblesum; //bildet die quadradsumme über den nibbles if (verbosemode == 1) printf ("f[%u]=%3.5f, %3.5f", nibblecnt, nibblesum, nibblesquaresum); } // MAGIC Pokertest fips pub 140 !!! result = (16.0 / 5000.0 * nibblesquaresum) - 5000.0; // result<1.03 || result >57.4 (LINT mag das nicht) if (!((result > 1.03) && (result < 57.4))) { result *= -1.0; if (verbosemode == 1) printf ("\nTest nicht bestanden, da %3.5f nicht im Intervall [1,03;57,4]\n", result * -1); } else { if (verbosemode == 1) printf ("\nTest bestanden, da %3.5f im Intervall [1,03;57,4]\n", result); } /* falls test nicht bestanden wurde wird negative Anzahl zurückgeliefert*/ return result; } /*----------------------------------------------------------------*/ /* char runtest(__u8 *inputbuffer) */ /* Typ: Funktion */ /* Parameter: inputbuffer: Zeiger auf Puffer */ /* */ /* Funktion: prüft auf ketten von nullen bzw. einsen */ /* falls Ketten der Länge >34 auftreten ist der */ /* rückgabewert negativ. */ /* falls die Anzahl einer Klasse von ketten außerhalb */ /* der testgrenzen lag wird |5| zurückgeliefert */ /* */ /* */ /*----------------------------------------------------------------*/ char runtest (__u8 * inputbuffer, int done) { int cheatlint = 0; // take ignorable returnvalues int intervals[7][2] = // TESTVORGABE { { 0, 20000 } , { 2267, 2733 } , { 1079, 1421 } , { 502, 748 } , { 223, 402 } , { 90, 223 } , { 90, 223 } }; int runlen[2][600] = //alle längen NTK (nice to know) { { 0, 0, } , { 0, 0, } }; int runlen6[2] = // längen >6 { 0, 0 }; int bitsum[2] = // kleines zusatzfeature zum erneuten zählen von bits { 0, 0 }; int bits = 0, runcount = 0; // zähler char result = (char) 1; // rückgabewert __u8 bit = (__u8) 0, oldbit = (__u8) 0; // bitsspeicher char bestanden1 = 'o', bestanden2 = 'o'; // char wegen einsparung v. Speicher /* initialisierung */ for (runcount = 0; runcount < 600; runcount++) { runlen[0][runcount] = 0; runlen[1][runcount] = 0; } runcount = 0; if (verbosemode == 1) printf ("\n\nMache Runtest:\n0%%----------------------50%%---------------------100%%\n["); for (bits = 0; bits < 20000; bits++) { if (bits % 400 == 0) if (verbosemode == 1) { printf ("."); cheatlint = fflush (stdout); } bit = readbit (inputbuffer, bits); //liest ein ein bit an der "bits-ten" stelle if (bits) // ungleich (>) null! bedeutet wird nicht beim ersten Durchlauf ausgeführt { if (oldbit == bit) { //ist gelesenes bit gleich letztem ?? ja erhöhe die anzahl(kettenlänge) um eins runcount++; } else { // schreibe kette in kettenzähler if (runcount > 599) runcount = 599; runlen[(int) oldbit][runcount]++; if (runcount >= 6) runlen6[(int) oldbit]++; runcount = 1; } } oldbit = bit; } // letzten run registrieren if (runcount > 599) // ausserhalb des zul. Bereichs runcount = 599; runlen[(int) oldbit][runcount]++; if (runcount > 6) runlen6[(int) oldbit]++; // Ketten der Länge >=6 noch summieren if (verbosemode == 1) printf ("]\n\nLänge\t 0\t 1\n"); if (verbosemode == 1) printf ("----------------------\n"); runlen[0][6] = runlen6[0]; runlen[1][6] = runlen6[1]; for (runcount = 1; runcount < 600; runcount++) // leere Fächer nicht anzeigen if ((runlen[0][runcount] > 0) || (runlen[1][runcount] > 0)) { if (runcount <= 6) { if (runlen[0][runcount] > intervals[runcount][0] && runlen[0][runcount] < intervals[runcount][1]) bestanden1 = 'o'; else bestanden1 = 'x'; if (runlen[1][runcount] > intervals[runcount][0] && runlen[1][runcount] < intervals[runcount][1]) bestanden2 = 'o'; else bestanden2 = 'x'; if (bestanden1 == 'x' || bestanden2 == 'x') result = (char) 5; if (verbosemode == 1) { if (runcount < 6) printf ("%4d\t%4d(%c)\t%4d(%c)\t [%d;%d]\n", runcount, runlen[0][runcount], bestanden1, runlen[1][runcount], bestanden2, intervals[runcount][0], intervals[runcount][1]); else printf (">=%2d\t%4d(%c)\t%4d(%c)\t [%d;%d]\n", 6, runlen6[0], bestanden1, runlen6[1], bestanden2, intervals[6][0], intervals[6][1]); } } if (runcount > 6) { if (verbosemode == 1) //ausgabefunktionen (keine berechnung) printf ("%4d\t%4d\t%4d\n", runcount, runlen[0][runcount], runlen[1][runcount]); } // test auf longruns if (runcount > 34) { if (verbosemode == 1) printf ("\n---LONG-RUNS---\n"); result = -1*result; // result: 1=runtest bestanden, 5 durchgefallen, < 0: longruntest nicht bestanden }; // summpliziere bits bitsum[0] += (runcount + 1) * runlen[0][runcount]; bitsum[1] += (runcount + 1) * runlen[1][runcount]; } if (verbosemode == 1) printf ("----------------------\n%5d\t%5d\t%5d\n----------------------\n", bitsum[0] + bitsum[1], bitsum[0], bitsum[1]); return (result); // 1=bestanden |5| durchgefallen "-" longruntest nicht bestanden } /*----------------------------------------------------------------*/ /* double autokorrelationstest(__u8 *inputbuffer) */ // Achtung: Getestet wird das XOR der Bits, also Modulo-2-Addition, während // bei der Autokorrelation nur das Produkt verwendet wird! /* Typ: Funktion */ /* Parameter: inputbuffer: Zeiger auf Puffer */ /* */ /* Funktion: Gibt einen durchschittswert der Autokorrelation zurück */ /* */ /* */ /* ein negativer Wert zeigt an, daß eine von 5000 */ /* Summen nicht in den testgrenzen lag */ /* */ /* */ /*----------------------------------------------------------------*/ double autokorrelationstest(__u8 *inputbuffer,int done) { int fucklint=0; // take ignorable returnvalues int cnt,cnt2,sum=0, error=1; double sumsum=0, d_acf_sum=0; if (verbosemode==1) printf("\n\nMache Autokorrelationstest:\n0%%----------------------50%%---------------------100%%\n["); // for bit 0 to 5000 for (cnt=0; cnt<5000; cnt++) { if (done==0) if (verbosemode==1) printf("."); fucklint=fflush(stdout); // LiNt LInt s.o. // calculate autocorrelation without normalisation for (cnt2=0; cnt2<5000; cnt2++) { sum += (int)(readbit(inputbuffer,cnt)^readbit(inputbuffer,cnt2)); d_acf_sum += (double)(readbit(inputbuffer,cnt)*readbit(inputbuffer,cnt2)); } if ( sum<2326 or sum>2674 ) { if (verbosemode==1) printf("\n Fehler beim %dsten Durchlauf summe: %d < 2326 or %d > 2674.\n", cnt, sum, sum); error=-1; } sumsum+=(double)sum; sum=0; } if (not quietmode) fprintf( stdout, "Average xor of two bits: %f (should be slightly lower than 0.5).\n", sumsum/(5000.*5000.)); return (((double)error)*d_acf_sum/(5000.*5000.)); } // autocorrelation of the bytes double autocorrelation8(__u8 *inputbuffer) { int cnt,cnt2,sum=0;//, error=1; double sumsum=0; // for byte 0 to 1249 for (cnt=0; cnt<1250; cnt++) { // calculate autocorrelation without normalisation for (cnt2=0; cnt2<1250; cnt2++) { sum+=readbyte(inputbuffer,cnt)*readbyte(inputbuffer,cnt2); } sumsum+=(double)sum; sum=0; } return (sumsum/(1250.0*1250.0)); } // autocorrelation of the words double autocorrelation16(__u8 *inputbuffer) { int cnt,cnt2;//, error=1; double sumsum=0, d_sum=0; // for word 0 to 624 for (cnt=0; cnt<625; cnt++) { // calculate autocorrelation without normalisation for (cnt2=0; cnt2<625; cnt2++) { d_sum += (double)readword((__u16*)inputbuffer, cnt)*(double)readword((__u16*)inputbuffer, cnt2); } sumsum += d_sum; d_sum=0; } return (sumsum/(625.0*625.0)); } int main (const int argc, const char * const argv[]) { int result = 0, i=0, j=0; double sumpoker = 0.0, d_sum=0., d_tmp=0; double sumentropy=0.,sumautokor=0.; int summono = 0; char sumrun = 0; int cheatlint = 0; // take ignorable return values __u8 *filecontent = NULL; // streambuffer struct stat filebuffer; size_t gimme_a_big_part_of_mem = 0; FILE *datei = NULL; // pointer to file if (argc < 2 or argc > 4) { fprintf (stderr, "usage : %s [-q] [-v] [-V] [filename (-q:quietmode -v:verbose(overides -q))], with file size > 2500 Byte.\n", argv[0]); fprintf (stderr, "[] = Optional <> = Parameter Requirement. At minimum one parameter is required.\n"); exit (-4); } // Parse argumente if (argc >= 2) { if ( not strncmp (argv[1], "-V", 123) ) { fprintf (stdout, "%s version %.1f\n", argv[0], SOFTWARE_VERSION_NUMBER); exit ( (2 == argc) ? 0 : -1); } if ((3==argc) and (0 == strncmp (argv[1], "-q", 123) or 0 == strcmp (argv[2], "-q"))) { quietmode = 1; verbosemode = 0; } if ((3==argc) and (0 == strncmp (argv[1], "-v", 123) or 0 == strcmp (argv[2], "-v"))) { verbosemode = 1; quietmode = 0; } } if (argv[argc - 1] and (-1 == stat (argv[argc - 1], &filebuffer))) { fprintf (stderr, "stat(%s) failed; can't open file \"%s\", exiting.\n", argv[argc - 1], argv[argc - 1]); perror ("lstat"); exit(-1); } gimme_a_big_part_of_mem = (size_t) filebuffer.st_size; if (gimme_a_big_part_of_mem < 2500) // Nach FIPS PUB 140-1, 140-2 werden nur 20000 Bit getestet; hier werden dafür die ersten Bits genommen { if (datei) cheatlint = fclose (datei); fprintf (stderr, "\nFehler: Eingabedatei < 20000 Bit!\n"); exit (-2); } gimme_a_big_part_of_mem = 2500; // 2500 Byte = 20000 Bit if (verbosemode == 1) printf ("\n%d bits werden gelesen\n", gimme_a_big_part_of_mem * 8); // get memory for 20000 Bit filecontent = (__u8 *) malloc (gimme_a_big_part_of_mem); if (filecontent == NULL) { if (datei) cheatlint = fclose (datei); fprintf (stderr, "\nmalloc failed (not enough memory), exiting!\n"); exit (-2); } if ((NULL == argv[argc - 1]) or (not(datei = fopen (argv[argc - 1], "rb")))) { fprintf (stderr, "\n%s nicht gefunden\n", argv[argc - 1]); exit (-1); } // open file for reading and exit with message if not found. Read the 20000 first bits. cheatlint = (int) fread (filecontent, 1, gimme_a_big_part_of_mem, datei); if (verbosemode == 1) printf ("\n%d bits gelesen\n", gimme_a_big_part_of_mem * 8); cheatlint = fclose (datei); summono = monobittest (filecontent, 1); sumpoker = pokertest (filecontent, gimme_a_big_part_of_mem); sumrun = runtest (filecontent, 1); // runtest must have error on fff or 111 files if (quietmode == 0) printf ("%3d\t%3.3f\t%3d\t\n", summono, sumpoker, (int) sumrun); free (filecontent); if (summono < 1) result |= 2; if (sumpoker < 1) result |= 4; if (abs ((int) sumrun) > 1) result |= 8; if (sumrun < 1) result |= 16; if (not quietmode) { if (result) { printf ("\nTest nicht bestanden Exitcode:%d\n", result); if (summono < 1) fprintf(stdout, "Monobittest failed.\n"); if (sumpoker < 1) fprintf(stdout, "Pokertest failed.\n"); if (abs ((int) sumrun) > 1) fprintf(stdout, "Runstest failed.\n"); if (sumrun < 0) fprintf(stdout, "Longrunstest failed.\n"); } else printf ("\nFips-140-1-tests successfully passed!\n"); sumentropy=entropietest(filecontent,gimme_a_big_part_of_mem); fprintf(stdout, "Byte-wise entropy (in bit/byte): %f (should be slightly lower than 8).\n", sumentropy); if ((int)sumentropy<7.) // failed fprintf(stdout, "Entropy test failed: %f < 7.\n", sumentropy); sumautokor=autokorrelationstest(filecontent, 1); if (sumautokor <= 0.) fprintf(stdout, "Bit autocorrelation Test failed, %f.\n", sumautokor); // calculate the theory value of byte autocorrelation d_sum = 0.; for (i=0; i<0x100; i++) for (j=0; j<0x100; j++) d_sum += ((double)i) * ((double)j); d_sum /= 0x10000; // normalise d_tmp = autocorrelation8(filecontent); fprintf(stdout, "Average byte autocorrelation/theory value: %f", d_tmp/d_sum); fprintf (stdout, " \t(should be close to 1).\n"); if (d_tmp/d_sum < .95) fprintf (stdout, "Byte autocorrelation test FAILED: %f < 0.95 !\n", d_tmp/d_sum); if (d_tmp/d_sum > 1.05) fprintf (stdout, "Byte autocorrelation test FAILED: %f > 1.05 !\n", d_tmp/d_sum); // calculate the theory value of word autocorrelation d_sum = 0.; for (i=0; i<0x10000; i++) for (j=0; j<0x10000; j++) d_sum += ((double)i) * ((double)j); d_sum /= 0x100000000p0; // normalise d_tmp = autocorrelation16(filecontent); fprintf(stdout, "Average word autocorrelation/theory value: %f", d_tmp/d_sum); fprintf (stdout, " \t(should be close to 1).\n"); if (d_tmp/d_sum < .95) fprintf (stdout, "Word autocorrelation test FAILED: %f < 0.95 !\n", d_tmp/d_sum); if (d_tmp/d_sum > 1.05) fprintf (stdout, "Word autocorrelation test FAILED: %f > 1.05 !\n", d_tmp/d_sum); } exit (result); }