/** ** Simple entropy harvester based upon the havege RNG ** ** Copyright 2009-2014 Gary Wuertz gary@issiweb.com ** Copyright 2011-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 isolates the operation of the HAVEGE algorithm to better * deal with compiler issues. Extensive macro expansion used to deal with * hardware variations. */ #include "config.h" #include #include #include #include "havegecollect.h" #include "havegetest.h" #include "havegetune.h" /** * Injection and capture diagnostics */ #if defined(RAW_IN_ENABLE) || defined(RAW_OUT_ENABLE) #define DIAGNOSTICS_ENABLE #endif /** * Option to use clockgettime() as timer source */ #if defined(ENABLE_CLOCK_GETTIME) #include #undef HARDCLOCK #define HARDCLOCK(x) x = havege_clock() /** * Provide a generic timer fallback */ static H_UINT havege_clock(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (H_UINT)(ts.tv_nsec + ts.tv_sec * 1000000000LL); } #endif /** * Memory allocation sizing */ #define SZH_INIT sizeof(H_COLLECT)+sizeof(char *)*(LOOP_CT + 2) #define SZH_COLLECT(a) sizeof(H_COLLECT)+sizeof(H_UINT)*(a+16384-1) /** * The HAVEGE collector is created by interleaving instructions generated by * oneiteration.h with the LOOP() output to control the sequence. At each * LOOP() point, the following actions are possible. */ typedef enum { LOOP_NEXT, /* Next loop */ LOOP_ENTER, /* First loop */ LOOP_EXIT /* Last loop */ } LOOP_BRANCH; /** * The LOOP macro labels calculation sequences generated by oneiteration.h in * decreasing order from LOOPCT down to 0. During a normal collection, the * loop construct only introduces an extra conditional branch into the instruction * stream. For the exceptional conditions (initialization, end-of-loop, and * raw HARDCLOCK capture), the return from havege_cp() is used to determine * the action to be taken. */ #define LOOP(n,m) loop##n: if (n < h_ctxt->havege_cdidx) { \ switch(havege_cp(h_ctxt,i,n,LOOP_PT(n))) { \ case LOOP_NEXT: goto loop##m; \ case LOOP_ENTER: goto loop_enter; \ case LOOP_EXIT: goto loop_exit; \ } \ } /** * These macros below bind the code contained in oneiteration.h to the H_COLLECT * instance defined above */ #define ANDPT (h_ctxt->havege_andpt) #define PTTEST (h_ctxt->havege_PTtest) #define PT (h_ctxt->havege_PT) #define PT1 (h_ctxt->havege_pt2) #define PT2 (h_ctxt->havege_PT2) #define PWALK (h_ctxt->havege_pwalk) #define RESULT (h_ctxt->havege_bigarray) /** * Previous diagnostic support has been replaced. The new implementation provides * simultaneous access to both the noise source (i.e. the timer tics) and the * output. The tics buffer is also used by the injection diagnostic if enabled */ #ifdef DIAGNOSTICS_ENABLE #define HTICK1 (h_ctxt->havege_tics[i>>3]) #define HTICK2 (h_ctxt->havege_tics[i>>3]) #define SZ_TICK ((h_ptr->i_collectSz)>>3) #else #define HTICK1 (h_ctxt->havege_tic) #define HTICK2 (h_ctxt->havege_tic) #define SZ_TICK 0 #endif /** * If the injection diagnostic is enabled, use a wrapper for the timer source */ #ifdef RAW_IN_ENABLE static H_UINT havege_inject(H_COLLECT *h_ctxt, H_UINT x); #define HARDCLOCKR(x) x=havege_inject(h_ctxt, x) #else #define HARDCLOCKR(x) HARDCLOCK(x) #endif /** * inline optimization - left conditional for legacy systems */ #if 0 #define ROR32(value,shift) ((value >> (shift)) | (value << (32-shift))) #else inline static H_UINT ror32(const H_UINT value, const H_UINT shift) { return (value >> shift) | (value << (32 - shift)); } #define ROR32(value,shift) ror32(value, shift) #endif /** * Local prototypes */ static LOOP_BRANCH havege_cp(H_COLLECT *h_ctxt, H_UINT i, H_UINT n, char *p); /** * Protect the collection mechanism against ever-increasing gcc optimization */ #if defined (GCC_VERSION) && GCC_VERSION >= 40400 static int havege_gather(H_COLLECT * h_ctxt) __attribute__((optimize(1))); #else static int havege_gather(H_COLLECT * h_ctxt); #endif static void havege_ndinit(H_PTR h_ptr, struct h_collect *h_ctxt); /** * Create a collector */ H_COLLECT *havege_ndcreate(/* RETURN: NULL on failure */ H_PTR h_ptr, /* IN-OUT: application instance */ H_UINT nCollector) /* IN: The collector instance */ { H_UINT i,offs,*p,d_cache; H_UINT szBuffer; H_COLLECT *h_ctxt; szBuffer = h_ptr->i_collectSz; d_cache = ((CACHE_INST *)(h_ptr->dataCache))->size; h_ctxt = (H_COLLECT *) calloc(SZH_COLLECT(szBuffer + SZ_TICK),1); if (NULL != h_ctxt) { h_ctxt->havege_app = h_ptr; h_ctxt->havege_idx = nCollector; h_ctxt->havege_raw = h_ptr->havege_opts & 0xff00; h_ctxt->havege_rawInput = h_ptr->inject; h_ctxt->havege_szCollect = szBuffer; h_ctxt->havege_szFill = szBuffer>>3; h_ctxt->havege_cdidx = h_ptr->i_idx; p = (H_UINT *) RESULT; h_ctxt->havege_err = H_NOERR; h_ctxt->havege_tests = 0; h_ctxt->havege_extra = 0; h_ctxt->havege_tics = p+szBuffer; /** An intermediate walk table twice the size of the L1 cache is allocated ** for use in permuting time stamp readings. The is meant to exercise ** processor TLBs. */ ANDPT = ((2*d_cache*1024)/sizeof(H_UINT))-1; p = (H_UINT *) calloc((ANDPT + 4097)*sizeof(H_UINT),1); if (NULL != p) { h_ctxt->havege_extra = p; offs = (H_UINT)((((unsigned long)&p[4096])&0xfff)/sizeof(H_UINT)); PWALK = &p[4096-offs]; /** * Warm up the generator, running the startup tests */ #if defined(RAW_IN_ENABLE) if (0 == (h_ctxt->havege_raw & H_DEBUG_TEST_IN)) #endif { H_UINT t0=0; (void)havege_gather(h_ctxt); /* first sample */ t0 = HTICK1; for(i=1;ierror = H_NOTIMER; havege_nddestroy(h_ctxt); return NULL; } } #ifdef ONLINE_TESTS_ENABLE { procShared *ps = (procShared *)(h_ptr->testData); while(0!=ps->run(h_ctxt, 0)) { /* run tot tests */ (void)havege_gather(h_ctxt); } } if (H_NOERR != (h_ptr->error = h_ctxt->havege_err)) { havege_nddestroy(h_ctxt); return NULL; } #endif h_ctxt->havege_nptr = szBuffer; if (0 == (h_ctxt->havege_raw & H_DEBUG_RAW_OUT)) h_ctxt->havege_szFill = szBuffer; } else { havege_nddestroy(h_ctxt); h_ptr->error = H_NOWALK; return NULL; } } else h_ptr->error = H_NOCOLLECT; return h_ctxt; } /** * Destruct a collector */ void havege_nddestroy( /* RETURN: none */ H_COLLECT *h_ctxt) /* IN: collector context */ { if (0 != h_ctxt) { if (h_ctxt->havege_extra!=0) { free(h_ctxt->havege_extra); h_ctxt->havege_extra = 0; } if (h_ctxt->havege_tests!=0) { free(h_ctxt->havege_tests); h_ctxt->havege_tests = 0; } free((void *)h_ctxt); } } /** * Read from the collector. */ H_UINT havege_ndread( /* RETURN: data value */ H_COLLECT *h_ctxt) /* IN: collector context */ { if (h_ctxt->havege_nptr >= h_ctxt->havege_szFill) { H_PTR h_ptr = (H_PTR)(h_ctxt->havege_app); pMeter pm; if (0 != (pm = h_ptr->metering)) (*pm)(h_ctxt->havege_idx, 0); #ifdef ONLINE_TESTS_ENABLE { procShared *ps = (procShared *)(h_ptr->testData); do { (void) havege_gather(h_ctxt); (void) ps->run(h_ctxt, 1); } while(ps->discard(h_ctxt)>0); } #else (void) havege_gather(h_ctxt); #endif h_ptr->n_fills += 1; if (0 != pm) (*pm)(h_ctxt->havege_idx, 1); h_ctxt->havege_nptr = 0; } #ifdef RAW_OUT_ENABLE if (0!=(h_ctxt->havege_raw & H_DEBUG_RAW_OUT)) return h_ctxt->havege_tics[h_ctxt->havege_nptr++]; #endif return RESULT[h_ctxt->havege_nptr++]; } /** * Setup haveged */ void havege_ndsetup( /* RETURN: None */ H_PTR h_ptr) /* IN-OUT: application instance */ { char wkspc[SZH_INIT]; memset(wkspc, 0, SZH_INIT); havege_ndinit(h_ptr, (struct h_collect *) wkspc); } /** * This method is called only for control points NOT part of a normal collection: * * a) For a collection loop after all iterations are performed, this function * determines if the collection buffer is full. * b) For initialization, this method saves the address of the collection point * for analysis at the end of the loop. */ static LOOP_BRANCH havege_cp( /* RETURN: branch to take */ H_COLLECT *h_ctxt, /* IN: collection context */ H_UINT i, /* IN: collection offset */ H_UINT n, /* IN: iteration index */ char *p) /* IN: code pointer */ { if (h_ctxt->havege_cdidx <= LOOP_CT) return i < h_ctxt->havege_szCollect? LOOP_ENTER : LOOP_EXIT; ((char **)RESULT)[n] = CODE_PT(p); if (n==0) h_ctxt->havege_cdidx = 0; return LOOP_NEXT; } /** * The collection loop is constructed by repetitions of oneinteration.h interleaved * with control points generated by the LOOP macro. */ static int havege_gather( /* RETURN: 1 if initialized */ H_COLLECT * h_ctxt) /* IN: collector context */ { H_UINT i=0,pt=0,inter=0; H_UINT *Pt0, *Pt1, *Pt2, *Pt3, *Ptinter; #if defined(RAW_IN_ENABLE) if (0 != (h_ctxt->havege_raw & H_DEBUG_RAW_IN)) { (*h_ctxt->havege_rawInput)(h_ctxt->havege_tics, h_ctxt->havege_szCollect>>3); h_ctxt->havege_tic = h_ctxt->havege_tics[0]; } else if (0 != (h_ctxt->havege_raw & H_DEBUG_TEST_IN)) { (*h_ctxt->havege_rawInput)(RESULT, h_ctxt->havege_szCollect); return 1; } #endif loop_enter: LOOP(40,39) #include "oneiteration.h" LOOP(39,38) #include "oneiteration.h" LOOP(38,37) #include "oneiteration.h" LOOP(37,36) #include "oneiteration.h" LOOP(36,35) #include "oneiteration.h" LOOP(35,34) #include "oneiteration.h" LOOP(34,33) #include "oneiteration.h" LOOP(33,32) #include "oneiteration.h" LOOP(32,31) #include "oneiteration.h" LOOP(31,30) #include "oneiteration.h" LOOP(30,29) #include "oneiteration.h" LOOP(29,28) #include "oneiteration.h" LOOP(28,27) #include "oneiteration.h" LOOP(27,26) #include "oneiteration.h" LOOP(26,25) #include "oneiteration.h" LOOP(25,24) #include "oneiteration.h" LOOP(24,23) #include "oneiteration.h" LOOP(23,22) #include "oneiteration.h" LOOP(22,21) #include "oneiteration.h" LOOP(21,20) #include "oneiteration.h" LOOP(20,19) #include "oneiteration.h" LOOP(19,18) #include "oneiteration.h" LOOP(18,17) #include "oneiteration.h" LOOP(17,16) #include "oneiteration.h" LOOP(16,15) #include "oneiteration.h" LOOP(15,14) #include "oneiteration.h" LOOP(14,13) #include "oneiteration.h" LOOP(13,12) #include "oneiteration.h" LOOP(12,11) #include "oneiteration.h" LOOP(11,10) #include "oneiteration.h" LOOP(10,9) #include "oneiteration.h" LOOP(9,8) #include "oneiteration.h" LOOP(8,7) #include "oneiteration.h" LOOP(7,6) #include "oneiteration.h" LOOP(6,5) #include "oneiteration.h" LOOP(5,4) #include "oneiteration.h" LOOP(4,3) #include "oneiteration.h" LOOP(3,2) #include "oneiteration.h" LOOP(2,1) #include "oneiteration.h" LOOP(1,0) #include "oneiteration.h" LOOP(0,0) (void)havege_cp(h_ctxt, i,0,LOOP_PT(0)); loop_exit: return ANDPT==0? 0 : 1; } #ifdef RAW_IN_ENABLE /** * Wrapper for noise injector. When input is injected, the hardclock * call is not made and the contents of the tic buffer are used * unchanged from when the inject call was made at the top of the * loop. */ static H_UINT havege_inject( /* RETURN: clock value */ H_COLLECT *h_ctxt, /* IN: workspace */ H_UINT x) /* IN: injected value */ { if (0==(h_ctxt->havege_raw & H_DEBUG_RAW_IN)) { HARDCLOCK(x); } return x; } #endif /** * Initialize the collection loop */ #if defined (GCC_VERSION) && GCC_VERSION >= 40600 #pragma GCC diagnostic ignored "-Warray-bounds" #endif static void havege_ndinit( /* RETURN: None */ H_PTR h_ptr, /* IN-OUT: application instance */ struct h_collect *h_ctxt) /* IN: workspace */ { char **addr = (char **)(&RESULT[0]); H_UINT sz; int i; h_ctxt->havege_cdidx = LOOP_CT + 1; (void)havege_gather(h_ctxt); for (i=0;i<=LOOP_CT;i++) { if (0 != (h_ptr->havege_opts & H_DEBUG_COMPILE)) { h_ptr->print_msg("Address %u=%p\n", i, addr[i]); } RESULT[i] = abs(addr[i] - addr[LOOP_CT]); if (i > 0 && 0 != (h_ptr->havege_opts & H_DEBUG_LOOP)) { h_ptr->print_msg("Loop %u: offset=%u, delta=%u\n", i,RESULT[i],RESULT[i-1]-RESULT[i]); } } h_ptr->i_maxidx = LOOP_CT; h_ptr->i_maxsz = RESULT[1]; sz = ((CACHE_INST *)(h_ptr->instCache))->size * 1024; for(i=LOOP_CT;i>0;i--) if (RESULT[i]>sz) break; h_ptr->i_idx = ++i; h_ptr->i_sz = RESULT[i]; }