/** ** Simple entropy harvester based upon the havege RNG ** ** Copyright 2018-2021 Jirka Hladky hladky DOT jiri AT gmail DOT com ** Copyright 2012-2014 Gary Wuertz gary@issiweb.com ** Copyright 2012 BenEleventh Consulting manolson@beneleventh.com ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /** * This compile unit implements online tests for haveged through the public functions tests*. * Online tests are run directly against the contents of the collection buffer immediately after * a buffer fill. Because collection buffer size does not have any direct relationship with * the data requirements of the individual tests, all tests implement a state machine to * handle segmented input. * * Note code directly related to the havege interface has been moved to a conditional * in that unit for easier maintainability. */ #include "config.h" #include #include #include #include "havegetest.h" #ifdef ONLINE_TESTS_ENABLE /** * Final value for aisSeq() when no transition found. Originally, Initially this used * INFINITY from but definition is undefined some gcc versions - foo! */ #define NO_TRANSITION 999999 /** * This structure is used only to pack the test structures into a single memory allocation. * This is necessary because some architectures have stringent alignment requirements that * cannot be met unless (compiler generated) padding is included. On mips in particular * double must be dword aligned or bus errors result. */ typedef struct { onlineTests olt; procA pa; procB pb; } testsMemory; /** * The tests and supporting methods */ static H_UINT aisProcedureA(H_COLLECT *h_ctxt, procShared *tps, procA *context, H_UINT *buffer, H_UINT sz, H_UINT offs, H_UINT prod); static H_UINT aisProcedureB(H_COLLECT *h_ctxt, procShared *tps, procB *context, H_UINT *buffer, H_UINT sz, H_UINT offs, H_UINT prod); static H_UINT aisSeq(procB *p, H_UINT offs, H_UINT id); static H_UINT aisTest(H_COLLECT * h_ctxt, H_UINT prod, H_UINT *buffer, H_UINT sz); static H_UINT copyBits(procA *p, H_UINT ct,H_UINT sz); static H_UINT fips140(procShared *tps, procA *p, H_UINT offs, H_UINT id); static H_UINT test0(procA *p, H_UINT offs, H_UINT id); static int test0cmp(const void *aa, const void *bb); static H_UINT test5(procA *p, H_UINT offs, H_UINT id); static H_UINT test5XOR(H_UINT8 *src, H_UINT shift); static H_UINT test6a(procB *p, H_UINT offs, H_UINT id); static H_UINT test8(procShared *tps, procB *p, H_UINT offs, H_UINT id); static int testsDiscard(H_COLLECT *rdr); static void testsMute(H_COLLECT * h_ctxt, H_UINT action, H_UINT prod, H_UINT state, H_UINT ct); static int testsRun(H_COLLECT *rdr, H_UINT prod); /** * The following suite of macros encapsulate the major bit operations of the test suite. * The intention is to write simple rather than clever code and let the optimizer strut * it's sutff. Note bit index starts with MSB for direct comparison with the test suit'e * Java reference implementation. */ #define BITSTREAM_BIT() ((*bitstream_src)&bitstream_in)==0? 0 : 1 #define BITSTREAM_NEXT() {if (bitstream_in==1) {\ bitstream_src+=1;\ bitstream_in=0x80;\ }\ else bitstream_in>>=1;} #define BITSTREAM_OPEN(a,b) H_UINT8 *bitstream_src=(H_UINT8 *)(a);\ H_UINT bitstream_in=0x80>>((b)%8);\ bitstream_src+=(b)/8 /** * Setup shared resources for online tests by sorting the test options into "tot" * and production groupings and allocating any resources used by the tests. * Caller is responsible for initializing the procShared structure with the * report, testsUsed, totTests[], runTests[], totText, and prodText fields. */ int havege_test( /* RETURN: nz on failure */ procShared *tps, /* IN-OUT: test anchor */ H_PARAMS *params) /* IN: app parameters */ { H_UINT i; tps->discard = testsDiscard; if (0==tps->report) tps->report = testsMute; tps->run = testsRun; tps->options = params->options; if (0!=(tps->testsUsed & A_RUN)) { H_UINT low[6] = {FIPS_RUNS_LOW}; H_UINT high[6] = {FIPS_RUNS_HIGH}; tps->procReps = 1 + (5 * AIS_A_REPS); for (i=0;i<6;i++) { tps->fips_low[i] = low[i]; tps->fips_high[i] = high[i]; } } if (0!=(tps->testsUsed & B_RUN)) { tps->G = (double *) malloc((Q+K+1)*sizeof(double)); if (0 == tps->G) return 1; tps->G[1] = 0.0; for(i=1; i<=(K+Q-1); i++) tps->G[i+1]=tps->G[i]+1.0/i; for(i=1; i<=(K+Q); i++) tps->G[i] /= LN2; } return 0; } /** * Check if the current buffer should be released if continuous testing is * being performed. The buffer must be discarded if it contains an * uncompleted retry or an uncompleted procedure with a failed test * or a failed procedure. */ static int testsDiscard( /* RETURN: non-zero to discard */ H_COLLECT * h_ctxt) /* IN-OUT: collector context */ { onlineTests *context = (onlineTests *) h_ctxt->havege_tests; procShared *tps = TESTS_SHARED(h_ctxt); procInst *p; H_UINT i; if (0==tps->testsUsed) return 0; if (context->result!=0) return -1; p = tps->runTests + context->runIdx; switch(p->action) { case A_RUN: if (0 != context->pA->procRetry) return 1; for (i = 0;ipA->testRun;i++) if (0 !=(context->pA->results[i].testResult & 1)) return 1; break; case B_RUN: if (0 != context->pB->procRetry) return 1; for (i=0;ipB->testNbr;i++) if (0!=(context->pB->results[i].testResult & 0xff)) return 1; break; } return 0; } /** * Place holder for when report is not configured */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" static void testsMute( H_COLLECT * h_ctxt, /* IN-OUT: collector context */ H_UINT action, /* IN: A_RUN or B_RUN */ H_UINT prod, /* IN: 0==tot, else continuous */ H_UINT state, /* IN: state variable */ H_UINT ct) /* IN: bytes consumed */ { ; } #pragma GCC diagnostic pop /** * The public wrapper that runs the tests. On the first call, the necessary machinery is built. * The calls to aisTest() actually run the tests. The test shared structure is read only in this * case, since testsRun could be called in a multi-threaded environment where an onlineTests * structure is associated with each collection thread. */ static int testsRun( /* RETURN: nz if input needed */ H_COLLECT * h_ctxt, /* IN-OUT: collector context */ H_UINT prod) /* IN: nz if production else tot */ { procShared *tps = TESTS_SHARED(h_ctxt); onlineTests *context; testsMemory *mem; procB *pb; if (0 ==(tps->testsUsed)) return 0; if (0 == h_ctxt->havege_tests) { H_UINT sz = sizeof(testsMemory); if (0==(tps->testsUsed & A_RUN)) sz -= sizeof(procA); if (0==(tps->testsUsed & B_RUN)) sz -= sizeof(procB); mem = (testsMemory *) malloc(sz); if (NULL==mem) { h_ctxt->havege_err = H_NOTESTMEM; return 1; } context = (onlineTests *) mem; memset(context, 0, sizeof(onlineTests)); if (0!=(tps->testsUsed & A_RUN)) { context->pA = &mem->pa; context->pA->procState = TEST_INIT; pb = &mem->pb; } else pb = (procB *)((void *) &mem->pa); if (0!=(tps->testsUsed & B_RUN)) { context->pB = pb; context->pB->procState = TEST_INIT; } h_ctxt->havege_tests = context; if (0 != (h_ctxt->havege_raw & H_DEBUG_TEST_IN)) return 0; } return aisTest(h_ctxt, prod, (H_UINT *)h_ctxt->havege_bigarray, h_ctxt->havege_szFill); } /** * AIS-31 test procedure A. The test is initiated by setting procState to TEST_INIT and * the test is performed by calling the procedure adding input until completion is indicated * by a proc state of TEST_DONE. The first test requires 3145728 bits (393,216 bytes) and * the remaining 5 tests are repeated on sucessive 2500 byte samples for 257 times. * * Exit states TEST_DONE, TEST_IGNORE, TEST_INPUT, TEST_RETRY * * An ideal RNG will pass this test with a probablilty of 0.9987. If there is a single failed * test, the test will be repeated. An ideal RNG should almost never fail the retry. The goal * of this procedure is to verify RNG output is statisically inconspicuous. */ static H_UINT aisProcedureA( /* RETURN: bits used */ H_COLLECT *h_ctxt, /* IN-OUT: collection instance */ procShared *tps, /* IN-OUT: shared data */ procA *p, /* IN: the context */ H_UINT *buffer, /* IN: the input */ H_UINT sz, /* IN: the input range */ H_UINT ct, /* IN: initial bit offset */ H_UINT prod) /* IN: production if nz */ { onlineTests *context = TESTS_CONTEXT(h_ctxt); H_UINT i, r; switch(p->procState) { case TEST_INIT: p->bytesUsed = 0; p->procRetry = 0; /* fallthrough */ case TEST_RETRY: p->procState = TEST_INPUT; p->testState = TEST_INIT; p->testId = p->testRun = 0; /* fallthrough */ case TEST_INPUT: p->data = (H_UINT8 *)buffer; p->range = sz * sizeof(H_UINT) <<3; while(p->testRun < tps->procReps) { p->testId = p->testRun<6? p->testRun : (1+(p->testRun-6) % 5); switch(p->testId) { case 0: ct = test0(p, ct, p->testRun); break; case 1: case 2: case 3: case 4: ct = fips140(tps, p, ct, p->testRun); break; case 5: ct = test5(p, ct, p->testRun); break; } context->szCarry = ct; if (p->testState == TEST_DONE) p->testState = TEST_INPUT; else if (p->testState == TEST_INPUT) return 0; } /* fallthrough */ case TEST_EVAL: p->procState = TEST_DONE; for (r = i = 0;itestRun;i++) r += p->results[i].testResult & 1; if (0!=r) { tps->meters[prod? H_OLT_PROD_A_F : H_OLT_TOT_A_F] += 1; if (1==r && 0==p->procRetry) { p->procRetry = 1; p->procState = TEST_RETRY; } else if (0!=(p->options & A_WARN)) p->procState = TEST_IGNORE; else { context->result = A_RUN; h_ctxt->havege_err = prod? H_NOTESTRUN : H_NOTESTTOT; } break; } else tps->meters[prod? H_OLT_PROD_A_P : H_OLT_TOT_A_P] += 1; if (0!=(tps->options & (H_DEBUG_OLT|H_DEBUG_OLT))|| TEST_DONE != p->procState) tps->report(h_ctxt, A_RUN, prod, p->procState, p->bytesUsed); break; } return p->bytesUsed<<3; } /** * AIS-31 test procedure B. The test is initiated by setting procState to TEST_INIT and * the test is performed by calling the procedure adding input until completion is indicated * by a proc state of TEST_DONE. Unlike procedure A, the number of input bits is not fixed * but depends on the input. * * Exit states TEST_DONE, TEST_IGNORE, TEST_INPUT, TEST_RETRY * * The probability that an ideal RNG will pass this test is 0.9998. If a single test fails, * the test is repeated. An ideal RNG should almost never fail the retry. The goal of this * procedure is to ensure the entropy of the output is sufficiently large. */ static H_UINT aisProcedureB( /* RETURN: bits used */ H_COLLECT *h_ctxt, /* IN-OUT: collection instance */ procShared *tps, /* IN-OUT: shared data */ procB *p, /* IN: the context */ H_UINT *buffer, /* IN: the input */ H_UINT sz, /* IN: the input range */ H_UINT ct, /* IN: initial bit offset */ H_UINT prod) /* IN: production if nz */ { onlineTests *context = TESTS_CONTEXT(h_ctxt); H_UINT i, r; switch(p->procState) { case TEST_INIT: p->bitsUsed = 0; p->procRetry = 0; /* fallthrough */ case TEST_RETRY: p->testId = p->testNbr = 0; p->procState = TEST_INPUT; p->testState = TEST_INIT; /* fallthrough */ case TEST_INPUT: p->noise = buffer; p->range = sz * BITS_PER_H_UINT; i = p->testId; while(p->testState != TEST_DONE && i < 5) { p->seq = 1<testState == TEST_INPUT) break; p->testId = ++i; p->testState = TEST_INIT; } context->szCarry = ct; if (p->testState == TEST_INPUT) return 0; /* fallthrough */ case TEST_EVAL: p->procState = TEST_DONE; for (i=r=0;itestNbr;i++) r += p->results[i].testResult & 1; if (0!=r) { tps->meters[prod? H_OLT_PROD_B_F : H_OLT_TOT_B_F] += 1; if (1==r && 0==p->procRetry) { p->procRetry = 1; p->procState = TEST_RETRY; } else if (0!=(p->options & B_WARN)) p->procState = TEST_IGNORE; else { context->result = B_RUN; h_ctxt->havege_err = prod? H_NOTESTRUN : H_NOTESTTOT; } } else tps->meters[prod? H_OLT_PROD_B_P : H_OLT_TOT_B_P] += 1; if (0!=(tps->options & H_DEBUG_OLT)|| TEST_DONE != p->procState) tps->report(h_ctxt, B_RUN, prod, p->procState, p->bitsUsed/8); break; } return p->bitsUsed; } /** * Driver for disjoint sequence tests - steps 2,3,4 of AIS-31 procedure B (aka test6b, test7a, and test7b). * Input tid is the width of the transition to be analyzed: tid=1 { 0x, 1x }, tid=2 {00x, 01x, 10x, 11x}, * tid=3 {000x, 001x, 010x, 011x, 100x, 101x, 110x, 111x}. The seq menber of procB gives # categories. * For a tuple of width n, transition probabilities are calculated for log2(n) transitions for the first * 100000 sequences. The deadman counter prevents total runaways with pathalogical input by counting * interations that fail to update any counter. If the deadman value exceeds the limit, evaluation of * the result forced. The probability of a forced evaluation is 10e-15. * * The macros below use fields in the procB structure to save/restore context when the input is * segmented. */ #define RESTORE(a,b,c) a=p->bridge;b=p->lastpos[0];c=p->lastpos[1] #define SAVE(a,b,c) p->bridge=a;p->lastpos[0]=b;p->lastpos[1]=c static H_UINT aisSeq( /* RETURN: last bit index */ procB *p, /* IN-OUT: the context */ H_UINT offs, /* IN: starting bit offset */ H_UINT tid) /* IN: test id == #bits */ { static const H_UINT seq_dead[5] = {0, 50, 120, 258, 0}; /* dead man limit */ static const H_UINT seq_mask[5] = {0, 3, 15, 255, 0}; /* full mask */ H_UINT i=0, c, deadman, r, s, j, hilf; switch(p->testState) { case TEST_INIT: for(j=0;jseq;j++) p->counter[j] = p->einsen[j] = 0; p->full = 0; p->testState = TEST_INPUT; SAVE(0,0,0); /* fallthrough */ case TEST_INPUT: RESTORE(j, hilf, deadman); offs %= p->range; r = p->range - offs; s = r % (tid+1); r -= s; { BITSTREAM_OPEN(p->noise,offs); while(ifull & (1< seq_dead[tid]) { p->testState = TEST_DONE; break; } } else { deadman = 0; p->einsen[hilf] += c; if ((p->counter[hilf]+=1)==AIS_LENGTH) { p->full |= (1<full == seq_mask[tid]) { p->testState = TEST_EVAL; break; } } } j = hilf = 0; } if (p->testState==TEST_INPUT) { for(j=hilf=0;jbitsUsed += i; if (p->testState == TEST_INPUT) break; /* fallthrough */ case TEST_EVAL: if (tid==1) { double q[2]; if (p->testState == TEST_EVAL) { for(j=0;j<2;j++) q[j] = (double)(p->einsen[j]) / (double) AIS_LENGTH; p->results[p->testNbr].finalValue = q[0] - q[1]; } else p->results[p->testNbr].finalValue = NO_TRANSITION; hilf = tid << 8; if (p->results[p->testNbr].finalValue <= -0.02 || p->results[p->testNbr].finalValue >= 0.02) hilf |= 1; p->results[p->testNbr++].testResult = hilf; } else { /** * The spec is very confusing but the reference implementation is correct. The test * operates on observed transions, i.e. the difference between pairs of successive * einsen and nullen (AIS_LENGTH - einsen) */ for(j=0; jseq; j+=2) { if (p->testState == TEST_EVAL) { double qn = (double)((int)p->einsen[j] - (int)p->einsen[j+1]); double qd = (double)(p->einsen[j] + p->einsen[j+1]); double pd = AIS_LENGTH * 2.0 - qd; p->results[p->testNbr].finalValue = ((qn * qn) / pd) + ((qn * qn) / qd); } else p->results[p->testNbr].finalValue = NO_TRANSITION; hilf = tid << 8; if (p->results[p->testNbr].finalValue > 15.13) hilf |= 1; p->results[p->testNbr++].testResult = hilf; } } p->testState = TEST_DONE; break; } return i+offs; } /** * Run the configured test procedures. This function cycles the procedure calls * setup by the configuration using tail recursion to sequence multiple tests. */ static H_UINT aisTest( /* RETURN: nz if input needed */ H_COLLECT * h_ctxt, /* IN-OUT: collector context */ H_UINT prod, /* IN: production indicator */ H_UINT *buffer, /* IN: test data, H_UINT aligned */ H_UINT sz) /* IN: size of data in H_UINT */ { procShared *tps = TESTS_SHARED(h_ctxt); onlineTests *context = (onlineTests *) h_ctxt->havege_tests; procInst *p; H_UINT offs,state=TEST_DONE, tot=0; if (context->result!=0) return 0; if (prod==0) p = tps->totTests + context->totIdx; else p = tps->runTests + context->runIdx; switch(p->action) { case A_RUN: if (context->pA->procState==TEST_INIT) context->pA->options = p->options; tot = aisProcedureA(h_ctxt, tps, context->pA, buffer, sz, context->szCarry, prod); state = context->pA->procState; break; case B_RUN: if (context->pB->procState==TEST_INIT) context->pB->options = p->options; tot = aisProcedureB(h_ctxt, tps, context->pB, buffer, sz, context->szCarry, prod); state = context->pB->procState; break; } if (state==TEST_INPUT) { context->szCarry = 0; return 1; } context->szTotal += tot; if (prod==0) { if (context->totIdx>=1) /* check for end of tot */ return 0; context->totIdx += 1; p = tps->totTests + context->totIdx; } else { if (0==tps->runTests[0].action) /* check for no cont tests */ return 0; else if (0!=tps->runTests[1].action) /* check for only 1 cont test */ context->runIdx = context->runIdx? 0 : 1; p = tps->runTests + context->runIdx; } switch(p->action) { case A_RUN: context->pA->procState=TEST_INIT; break; case B_RUN: context->pB->procState=TEST_INIT; break; } offs = context->szCarry/BITS_PER_H_UINT; if (offsszCarry -= offs * BITS_PER_H_UINT; return aisTest(h_ctxt, prod, buffer+offs, sz - offs); } return 1; } /** * Procedure A input is obtained by copying bits from p->data to p->aux using * p->bridge as position. This realigns the input to a byte boundary and * resolves segmentation issues. Originally implemented in BITSTREAM macros * performance was bad enough to justify serious tuning. Returns the updated * bit offset. */ /** * The BITSTREAM macros were totally inadequate for the proecedure A needs. These * helpers are used to implement a high performance copyBit(). */ #define COPY_BYTE() {c = (*src<>bit_diff_rs);src++;} #define COPY_FIRST() if ( (int) xfr >= (8 - dst_bits)) {\ *dst &= rm[dst_bits];\ xfr -= 8 - dst_bits;\ }\ else {\ *dst &= rm[dst_bits] | rm_xor[dst_bits + xfr + 1];\ c &= rm[dst_bits + xfr];\ xfr = 0;\ }\ xfr_bytes = xfr>>3;\ xfr_bits = xfr&7;\ *dst++ |= c; /** * Each procedure A repetition moves TEST0_USED + 257*FIPS_USED bits * to the auxilary work space - a little more than 1MB */ static H_UINT copyBits( /* RETURN: updated bit offset */ procA *p, /* IN-OUT: the context */ H_UINT offs, /* IN: the input bit offset */ H_UINT sz) /* IN: number of bits to copy */ { H_UINT avail = p->range; H_UINT need = sz - p->bridge; H_UINT xfer, xfr; offs %= avail; xfer = (avail-offs)data+(offs>>3); H_UINT8 *dst = p->aux +(p->bridge>>3); H_UINT8 src_bits = offs&7; H_UINT8 dst_bits = p->bridge&7; H_UINT xfr_bytes = xfr>>3; H_UINT xfr_bits = xfr&7; H_UINT8 c; if (src_bits==dst_bits) { if (src_bits!=0){ c = rm_xor[dst_bits] & *src++; COPY_FIRST(); } if (xfr_bytes!=0) { memcpy(dst, src, xfr_bytes); src += xfr_bytes; dst += xfr_bytes; } if (xfr_bits) { *dst &= rm_xor[xfr_bits]; *dst |= rm[xfr_bits] & *src++; } } else { H_UINT bit_diff_ls, bit_diff_rs; if (src_bits>dst_bits) { bit_diff_ls = src_bits - dst_bits; bit_diff_rs = 8 - bit_diff_ls; COPY_BYTE(); c &= rm_xor[dst_bits]; } else { bit_diff_rs = dst_bits - src_bits; bit_diff_ls = 8 - bit_diff_rs; c = *src >> bit_diff_rs & rm_xor[dst_bits]; } COPY_FIRST(); while (xfr_bytes-- != 0) { COPY_BYTE(); *dst++ = c; } if (xfr_bits!=0) { COPY_BYTE(); c &= rm[xfr_bits]; *dst &= rm_xor[xfr_bits]; *dst |= c; } } } p->bridge += xfer; if (p->bridge>=sz) { p->bytesUsed += sz>>3; p->bridge = 0; p->testState = TEST_EVAL; } return offs + xfer; } /** * Procedure A tests 1 through 4 correspond to the fips140-1 tests. These tests * are conducted on the same input stream, so the calculations can be * done in parallel. */ #define FIPS_ADD() {\ if (runLength < 5)\ runs[runLength + (6*current)]++;\ else runs[5 + (6*current)]++;\ } static H_UINT fips140( /* RETURN: updated bit offset */ procShared *tps, /* IN: shared data */ procA *p, /* IN-OUT: the context */ H_UINT offs, /* IN: starting offset */ H_UINT tid) /* IN: test id */ { H_UINT poker[16]; /* counters for poker test */ H_UINT ones; /* counter for monbit test */ H_UINT runs[12]; /* counters for runs tests */ H_UINT runLength; /* current run length */ H_UINT maxRun; /* largest run encountered */ H_UINT current; /* current bit */ H_UINT last; /* last bit index */ H_UINT c, i, j, k; switch(p->testState) { case TEST_INIT: p->testState = TEST_INPUT; p->bridge = 0; /* fallthrough */ case TEST_INPUT: offs = copyBits(p, offs, FIPS_USED); if (p->testState!=TEST_EVAL) break; /* fallthrough */ case TEST_EVAL: maxRun = ones = runLength = 0; memset(poker, 0, 16*sizeof(H_UINT)); memset(runs, 0, 12*sizeof(H_UINT)); { BITSTREAM_OPEN(p->aux,0); last = BITSTREAM_BIT(); for (c=i=0;imaxRun) maxRun = runLength; } else { FIPS_ADD(); runLength = 0; last = current; } c += c + current; ones += current; if (bitstream_in==1) { poker[c&15] += 1; c = 0; } else if (bitstream_in==16) poker[c] += 1; BITSTREAM_NEXT(); } FIPS_ADD(); } /* 1 = monobit test */ k = (ones >= FIPS_ONES_HIGH || ones <= FIPS_ONES_LOW)? 1 : 0; p->results[tid].testResult = k | (1<<8); p->results[tid++].finalValue = ones; /* 2 = poker test */ for(j=k=0;j<16;j++) k += poker[j]*poker[j]; j = (k <= FIPS_POKER_LOW || k >= FIPS_POKER_HIGH)? 1 : 0; p->results[tid].testResult = j | (2<<8); p->results[tid++].finalValue = k; /* 3 = runs test */ for(i=j=k=0;j<12;j++) if (runs[j] < tps->fips_low[j%6] || runs[j] > tps->fips_high[j%6]) { k |= 1; i = runs[j]; } p->results[tid].testResult = k | (3<<8); p->results[tid++].finalValue = i; /* 4 = max run length */ k = maxRun>=FIPS_MAX_RUN? 1 : 0; p->results[tid].testResult = k | (4<<8); p->results[tid++].finalValue = maxRun; p->testRun = tid; p->testState = TEST_DONE; } return offs; } /** * Procedure A disjointness test on 48 bit strings. Rejection probability for ideal * RNG is 2e^-17 */ static H_UINT test0( /* RETURN: updated bit offset */ procA *p, /* IN-OUT: the context */ H_UINT offs, /* IN: starting bit offset */ H_UINT tid) /* IN: test id */ { H_UINT i, j; switch(p->testState) { case TEST_INIT: p->testState = TEST_INPUT; p->bridge = 0; /* fallthrough */ case TEST_INPUT: offs = copyBits(p, offs, TEST0_USED); if (p->testState!=TEST_EVAL) break; /* fallthrough */ case TEST_EVAL: qsort(p->aux, TEST0_LENGTH, 6, test0cmp); for (i=6,j=0;iaux+i-6, p->aux+i, 6)) { j=1; } p->results[tid].testResult = j; p->results[tid++].finalValue = i; p->testRun = tid; p->testState = TEST_DONE; } return offs; } /** * Comparison method for the test0 sort */ static int test0cmp(const void *aa, const void *bb) { return memcmp(aa,bb,6); } /** * Procedure A autocorrelation test. Brutal bit twiddling. Uses same * data as FIPS - no update to bit offset */ static H_UINT test5( /* RETURN: updated bit offset */ procA *p, /* IN-OUT: the context */ H_UINT offs, /* IN: starting bit offset */ H_UINT tid) /* IN: test id */ { H_UINT8 *dp = (H_UINT8 *)p->aux; H_UINT j, k, max, tau, Z_tau; /** * Because this test is so slow it can be skipped on one or more repetitions */ if (0 != (p->options & A_CYCLE)) { j = p->options & A_CYCLE; if (j==0 || ((tid-1)/5 % j)!=0) { p->results[tid++].testResult = 0xff00; p->testRun = tid; p->testState = TEST_DONE; return offs; } } /** * This test always uses the same data as test1 through test4 */ for (max = k = 0,tau=1;tau<=TEST5_LENGTH;tau++){ Z_tau = abs( (int) test5XOR(dp, tau) - 2500); if (Z_tau > max) { max = Z_tau; k = tau - 1; } } dp += TEST5_LENGTH/8; Z_tau = test5XOR(dp, k + 1); j = 5<<8; if (( Z_tau <= 2326) || ( Z_tau >= 2674)) j |= 1; p->results[tid].testResult = j; p->results[tid++].finalValue = Z_tau; p->testRun = tid; p->testState = TEST_DONE; return offs; } /** * The test5 reference implementation looks something like this: * * for(i=0,j=shift;i>3]>>(i & 7))) ^ ((src[j>>3]>>(j & 7)))); * return rv; * * A high performance optimization using multi-byte casts is 3x as fast as the above but blows up * because of alignment issues (leftovers from the test0 implementation) * The optimized single byte optimization is 2x as fast as the above but uses no alignment games */ static H_UINT test5XOR(H_UINT8 *src, H_UINT shift) { H_UINT8 *src1; H_UINT i,rest, rv; src1 = src + (shift>>3); shift &= 7; rest = 8 - shift; for(i=rv=0;i<(TEST5_LENGTH>>3);i++) { H_UINT8 lw = *src++; H_UINT8 rw = *src1++; H_UINT8 w; for (w = (lw & (0xff>>shift)) ^ (rw>>shift);w!=0;w>>=1) rv += w & 1; for (w = (lw>>rest) ^ (*src1 & (0xff>>rest));w!=0;w>>=1) rv += w & 1; } return rv; } /** * Procedure B uniform distribution test for parameter set (1,100000,0.25). Very simple, just * count bits. Fixed input size, deadman not needed */ #define SIDEWAYS_ADD(c,i) {H_UINT in = i;\ in -= ((in >> 1) & 0x55555555);\ in = (in & 0x33333333) + ((in >> 2) & 0x33333333);\ c=(((in + (in >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;\ } static H_UINT test6a( /* RETURN: bit offset */ procB *p, /* IN-OUT: the context */ H_UINT offs, /* IN: starting bit offset */ H_UINT tid) /* IN: test id */ { H_UINT r = p->range - offs; H_UINT i=0,j=p->bridge; switch(p->testState) { case TEST_INIT: j = p->counter[0] = 0; p->testState = TEST_INPUT; /* fallthrough */ case TEST_INPUT: { BITSTREAM_OPEN(p->noise,offs); H_UINT c; /* align to a byte boundary, then shift gears to gobble bytes */ while(i < r && j < AIS_LENGTH && bitstream_in != 0x80){ p->counter[0] += BITSTREAM_BIT();BITSTREAM_NEXT(); i++;j++; } /* align to a word boundary, then shift gears to gobble words */ while((i+8) < r && (j+8) < AIS_LENGTH) { if (0==((char *)bitstream_src - (char *)p->noise) % sizeof(H_UINT)) break; SIDEWAYS_ADD(c, *bitstream_src++); p->counter[0] += c; i+=8;j+=8; } /* gobble all words available */ while((i+BITS_PER_H_UINT) < r && (j+BITS_PER_H_UINT) < AIS_LENGTH) { SIDEWAYS_ADD(c, *((H_UINT *)bitstream_src)); bitstream_src += sizeof(H_UINT); p->counter[0] += c; i+=BITS_PER_H_UINT;j+=BITS_PER_H_UINT; } /* shift back to bits & cleanup the leftovers */ for(;i < r && j < AIS_LENGTH;i++,j++) { p->counter[0] += BITSTREAM_BIT();BITSTREAM_NEXT(); } p->bitsUsed += i; if (j < AIS_LENGTH) { p->bridge = j; break; } } /* fallthrough */ case TEST_EVAL: p->results[p->testNbr].finalValue = (double)(p->counter[0]) / (double) AIS_LENGTH; r = tid << 8; if (p->results[p->testNbr].finalValue <= 0.25 || p->results[p->testNbr].finalValue >= 0.75) r |= 1; p->results[p->testNbr++].testResult = r; p->testState = TEST_DONE; } return i+offs; } /** * Context is saved and restored using inactive members of the anchor. */ #define RESTORE8(a,b,c,d) a=p->bridge;b=p->full;c=p->einsen[0];\ d=p->results[p->testNbr].finalValue #define SAVE8(a,b,c,d) p->bridge=a;p->full=b;p->einsen[0]=c;\ p->results[p->testNbr].finalValue = d /** * Procedure B entropy estimator (Coron). Find the distribution of the distance between * bytes and their predecessors. Fixed input size, no deadman needed. */ static H_UINT test8( /* RETURN: bit offset */ procShared *tps, /* IN-OUT: shared data */ procB *p, /* IN-OUT: the context */ H_UINT offs, /* IN: starting bit offset */ H_UINT tid) /* IN: test id */ { H_UINT hilf, j, k, r, i=0; double TG=0.0; switch(p->testState) { case TEST_INIT: memset(p->lastpos, 0, 256*sizeof(H_UINT)); SAVE8(0,0,0,0.0); p->testState = TEST_INPUT; /* fallthrough */ case TEST_INPUT: RESTORE8(k,j,hilf,TG); r = p->range - offs; { H_UINT align; /* gobble bits up to a byte boundary */ BITSTREAM_OPEN(p->noise,offs); for(;j<8 && i>align); bitstream_src++;i+=8;j=8; } for(;j<8 && ilastpos[hilf] = k++; else { TG += tps->G[k - p->lastpos[hilf]]; p->lastpos[hilf] = k++; if (k==(K+Q)) { p->testState = TEST_EVAL; break; } } j = hilf = 0; } if (p->testState==TEST_INPUT) { SAVE8(k,j,hilf,TG); } } p->bitsUsed += i; if (p->testState == TEST_INPUT) break; /* fallthrough */ case TEST_EVAL: tps->lastCoron = p->results[p->testNbr].finalValue = TG/(double)K; r = tid<<8; if (p->results[p->testNbr].finalValue <= 7.967) r |= 1; p->results[p->testNbr++].testResult = r; p->testState = TEST_DONE; } return i+offs; } #endif