/* Organization: Technical University of Kosice (TUKE), Department: Department of Electronics and Multimedia Telecommunications (DEMT/KEMT), Faculties: Faculty of Electrical Engineering and Informatics (FEI), Feld of study: Informatics, Study program: Computer Networks, School year: 3., Bachelor study, 2020/2021, Author: Marek Rohac -- MR, Compiler: Winlibs GCC -- MinGW-W64 x86_64-posix-seh, built by Brecht Sanders, v. 10.2.0, -- also works with GCC 11.1.0 compile: gcc winAPIprng.c -o rng -Wall -Wextra -Werror -g -std=c11 -lbcrypt, version: 1.2 , 20.05.2021. Description: This program is only for educational purposes. Program use 3 different windows APIs to generate random data cryptGenRandom, BCryptGenRandom and RtlGenRandom PLUS rand_s API for PRNG Usage: In macro RNG_PER_CYCLE_BITS define how much random bits you want by one cycle. Then define number of cycles in macro RNG_CYCLES Program then generate output according APIs and save values to .bin files with name by current API */ #define __USE_MINGW_ANSI_STDIO #include #define _CRT_RAND_S #include #include #include #include #include /* ULONG_MAX = 4096 MB --> 4. meranie (not possible with rand_s) 32MB = 268435456b --> 3. meranie 16MB = 134217728b --> 2. meranie 16KB = 131072b --> 1. meranie YOU CAN SET NUMBER OF BYTES IN RNG_BYTE_OUTPUT_SIZE TO AVOID CALCULATING TO BITS */ // bit size per API call .. #define RNG_PER_CYKLE_BITS 16*1024*1024*8 //number of calls --> 16gb now #define RNG_CYCLES 1024 // byte size of output #define RNG_BYTE_OUTPUT_SIZE RNG_PER_CYKLE_BITS/8 //uint size of output #define RNG_UINT_OUTPUT_SIZE RNG_PER_CYKLE_BITS/32 //we work with big endian values, there's check macro -- NOT USE, BUT USEFULL #define IS_BIG_ENDIAN (!*(unsigned char *)&(uint16_t){1}) //32 GB output --> 268435456*1024/8/1024/1024/1024 //8b = 1 BYTE //32b = 1 UINT /*############################################################ FUNCTIONS FOR MEASURMENTS ##############################################################*/ //MACROS FOR EASIER TIMER MEASURMENT, VIA WINAPI QueryPerformanceCounter #define TIMER_INIT \ LARGE_INTEGER frequency; \ LARGE_INTEGER t1,t2; \ double elapsedTime; \ QueryPerformanceFrequency(&frequency); /** Use to start the performance timer */ #define TIMER_START QueryPerformanceCounter(&t1); /** Use to stop the performance timer and output the result to the standard stream. Less verbose than \c TIMER_STOP_VERBOSE */ #define TIMER_STOP \ QueryPerformanceCounter(&t2); \ elapsedTime=(double)(t2.QuadPart-t1.QuadPart)/frequency.QuadPart; /* divide frequency.QuadPart by 1000.0 if you want time in ms*/ /* function to measure speed of generating process (output is number of number of cpu cycles) CYCLES * AverageCycles* 1/ghzOfProcessor * 10^-9 = cca execution CPUTIME Inspiration by --> https://github.com/newhopecrypto/newhope/tree/master/ref - MR: asm changed to __asm__ for newer c standard support (before we need -std=gnu99 to compile) */ /* int64_t cpucycles(void) { uint64_t result; //function use rdts instruction and shift higher values from reg where rdtsc store values //(rax, rdx) __asm__ volatile(".byte 15;.byte 49;shlq $32,%%rdx;orq %%rdx,%%rax" : "=a" (result) :: "%rdx"); return result; } ================================================================================= REPLACED BY CPUID/RDTSC/RDTSCP INSTRUCTIONS -->below-->cpucyclesS(),cpucyclesE() ***FOR BETTER PRECISIOUS*** ================================================================================= https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/ia-32-ia-64-benchmark-code-execution-paper.pdf CYCLES * AverageCycles* 1/ghzOfProcessor * 10^-9 = cca execution CPUTIME */ //cpucyclesS -- start measurment static __inline__ uint64_t cpucyclesS(){ unsigned cycles_low, cycles_high; __asm__ volatile ("CPUID\n\t" "RDTSC\n\t" "mov %%edx, %0\n\t" "mov %%eax, %1\n\t": "=r" (cycles_high), "=r" (cycles_low):: "%rax", "%rbx", "%rcx", "%rdx"); return (((uint64_t)cycles_high << 32) | cycles_low ); } //cpucyclesE -- end measurment static __inline__ uint64_t cpucyclesE(){ unsigned cycles_low, cycles_high; __asm__ volatile ("RDTSCP\n\t" "mov %%edx, %0\n\t" "mov %%eax, %1\n\t" "CPUID\n\t": "=r" (cycles_high), "=r" (cycles_low):: "%rax", "%rbx", "%rcx", "%rdx"); return (((uint64_t)cycles_high << 32) | cycles_low ); } /* compare for qsort */ static INT cmp_llu(const void *a, const void*b) { if(*(uint64_t *)a < *(uint64_t *)b) return -1; if(*(uint64_t *)a > *(uint64_t *)b) return 1; return 0; } /* calculating of median value from measurment */ static uint64_t median(uint64_t *l, size_t llen) { if (llen<=1) {printf("Not enought values for median\n"); return l[llen-1]; } qsort(l,llen,sizeof(uint64_t),cmp_llu); if(llen%2) return l[llen/2]; else return (l[llen/2-1]+l[llen/2])/2; } /* calculating of average value from measurment */ static uint64_t average(uint64_t *t, size_t tlen) { uint64_t acc=0; size_t i; for(i=0;i rand_s function on AMD processors */ void castUintToByte(uint32_t *uintArray,BYTE* output){ int i=0; int n=4; //because uint have 32b and BYTE 8b -- 32/8 = 4 for (uint32_t j = 0; j < RNG_UINT_OUTPUT_SIZE; j++){ for(; n-->0; i++) output[i]=(uintArray[j]>>(n*8))& 0xFF; n=4; } } //storing generated random data void store_Data(FILE * fp, void * pbBuffer){ fwrite(pbBuffer,sizeof(BYTE),RNG_BYTE_OUTPUT_SIZE,fp); if( feof(fp) ) {printf("Problem with %s file\n", (char*)fp);} //else printf("File successfully create!\n"); } //storing time datas of executions void store_Time_Data(const char * name, uint64_t *t, size_t tlen,FILE * fp, double programRun){ uint64_t sum; fprintf(fp, "%s\n",name); for(size_t i=0;i 32bits number= (uint32_t*) malloc(sizeof(uint32_t) * RNG_UINT_OUTPUT_SIZE); //memset(number,0,RNG_UINT_OUTPUT_SIZE*sizeof(uint32_t)); uint64_t time [RNG_CYCLES],tick; TIMER_INIT {TIMER_START for(int i=0; i < RNG_CYCLES; i++ ){ for( uint32_t j = 0; j < RNG_UINT_OUTPUT_SIZE;j++ ){ tick = cpucyclesS(); int err = rand_s(&number[j]); time[i] += cpucyclesE()-tick; //writing 32bits, so 4xBYTE, into variable number if(err != 0){ printf("The rand_s function failed!\n"); fclose(storeFile); free(number); return err; } } if(!IS_BIG_ENDIAN)castUintToByte(number,output); //correct casting from UINT to BYTE array if architecture use little endian //printBYTERandomArray("randS",output); //optional and not recomended for bigger outputs or measurments store_Data(storeFile,output); } TIMER_STOP} store_Time_Data("RAND_S:",time,RNG_CYCLES,timeFile, elapsedTime); fclose(storeFile); free(number); return 0; } /* Function demonstrate usage of deprecated API for RNG on windows --CRYPTGENRANDOM for more information visit: https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom */ int cryptgenrandom(BYTE * pbData, FILE * timeFile){ HCRYPTPROV hCryptProv; int check=0; FILE * storeFile=fopen("CryptGenRandom.bin","ab+"); uint64_t time[RNG_CYCLES],tick; TIMER_INIT {TIMER_START check=CryptAcquireContext(&hCryptProv,NULL,"Microsoft Base Cryptographic Provider v1.0",PROV_RSA_FULL,CRYPT_VERIFYCONTEXT); if (check){ check=0; for(int i=0; i < RNG_CYCLES; i++ ){ tick = cpucyclesS(); check += CryptGenRandom(hCryptProv,RNG_BYTE_OUTPUT_SIZE,pbData); time[i] = cpucyclesE() - tick; //printBYTERandomArray("CGRand",pbData); //optional and not recomended for bigger outputs or measurments store_Data(storeFile,pbData); } TIMER_STOP} store_Time_Data("CRYPTGENRANDOM:",time,RNG_CYCLES,timeFile, elapsedTime); fclose(storeFile); if (check==RNG_CYCLES){ check=CryptReleaseContext(hCryptProv,0); if (check){ return 0; } else{ printf("Error during CryptReleaseContext.\n"); return 3; } }else{ printf("Error during CryptGenRandom.\n"); CryptReleaseContext(hCryptProv,0); return 2; } }else{ printf("Error during CryptAcquireContext!\n"); return 1; } } /* BCryptGenRandom function 1. paramater is algorithm handler hAlgorithm The simplest way to initialize/use is insert NULL, then it will set handler on default provider Microsoft Base... Otherwise there are differnt option but first you will need to initialize that handler with function BCryptOpenAlgorithmProvider, which create handler for rng API. For more information read https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptopenalgorithmprovider 2. param is variable for store output 3. size of output in BYTES 4. param is flag. There are 2 options: BCRYPT_RNG_USE_ENTROPY_IN_BUFFER (0x00000001) function after flag above will use the number in the pbBuffer buffer as additional entropy for the random number. If this flag is not specified, this function will use a random number for the entropy. This flag is ignored in Windows 8 and later because of architecture changes in CNG working. BCRYPT_USE_SYSTEM_PREFERRED_RNG (0x00000002) Use the system-preferred random number generator algorithm. The hAlgorithm parameter must be NULL. On Windows Vista this flag is not supported. */ void bcrypt(BYTE * pbBuffer, FILE * timeFile){ FILE * storeFile=fopen("BCryptGenRandom.bin","ab+"); uint64_t time[RNG_CYCLES],tick; //for better precisious TIMER_INIT {TIMER_START for(int i=0; i < RNG_CYCLES; i++ ){ tick = cpucyclesS(); if (STATUS_SUCCESS!=BCryptGenRandom(NULL,pbBuffer,RNG_BYTE_OUTPUT_SIZE, BCRYPT_USE_SYSTEM_PREFERRED_RNG)){ printf("BCRYPTGENRANDOM ERROR in %d. cycle\n", i ); } time[i] = cpucyclesE() - tick; //printBYTERandomArray("bcrypt",pbBuffer); //optional and not recomended for bigger outputs or during measurment. store_Data(storeFile,pbBuffer); } TIMER_STOP} store_Time_Data("BCRYPTGENRANDOM:",time,RNG_CYCLES,timeFile, elapsedTime); fclose(storeFile); } void rtlgenrandom(BYTE * pbBuffer, FILE * timeFile){ FILE * storeFile=fopen("RtlGenRandom.bin","ab+"); uint64_t time[RNG_CYCLES],tick; TIMER_INIT {TIMER_START for(int i=0; i < RNG_CYCLES; i++ ){ tick = cpucyclesS(); if(RtlGenRandom(pbBuffer, RNG_BYTE_OUTPUT_SIZE)==0) printf("rtl err -1"); time[i] = cpucyclesE()- tick; //printBYTERandomArray("RtlGenRandom",pbBuffer); //optional and not recomended for bigger outputs or measurments store_Data(storeFile, pbBuffer); } TIMER_STOP} store_Time_Data("RTLGENRANDOM:",time,RNG_CYCLES,timeFile, elapsedTime); fclose(storeFile); } /*############################################################ END OF FUNCTIONS FOR GENERATING RANDOM VALUES VIA APIs ##############################################################*/ int main(){ /*############# INTIAL SETUP ########################*/ BYTE * pbData= NULL; pbData=(BYTE*) malloc(sizeof(BYTE) * RNG_BYTE_OUTPUT_SIZE); //memset(pbData,0,sizeof(BYTE) *1024* RNG_BYTE_OUTPUT_SIZE); //prepare memory FILE * timeFile=fopen("winapiResult.txt","a+"); // file for storing measurments TIMER_INIT {TIMER_START /*######## CALLING FUNCTIONS With APIs ##############*/ randS(pbData,timeFile); cryptgenrandom(pbData,timeFile); bcrypt(pbData,timeFile); rtlgenrandom(pbData,timeFile); /*###################### END ########################*/ TIMER_STOP} printf("Whole program executed in: %f sec\n",elapsedTime); fclose(timeFile); if (pbData) free(pbData); printf("COMPLETE!\n"); return 0; }