BachelorWork/appendixes/BC_ZK/winAPIprng.c
2021-05-24 22:44:55 +02:00

354 lines
12 KiB
C

/*
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
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 <stdio.h>
#include <stdint.h>
#include <windows.h>
#include <ntsecapi.h>
#include <ntstatus.h>
/*
64MB = 536870912b
ULONG_MAX = 4096 MB --> 4. meranie
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 --> 32gb 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<tlen;i++)acc += t[i];
if(acc!=0)return acc/(tlen);
return t[tlen];
}
/*############################################################
END OF FUNCTIONS FOR MEASURMENTS
##############################################################*/
/*############################################################
AUXILIARY FUNCTIONS
##############################################################*/
void printBYTERandomArray(const char * name, const void * buffer){
BYTE storage[RNG_BYTE_OUTPUT_SIZE];
memcpy(storage, buffer,RNG_BYTE_OUTPUT_SIZE);
printf("%s sequence generated:\n ",name);
for( int i = 0; i < RNG_BYTE_OUTPUT_SIZE;i++ )
printf("%02X",storage[i]);
printf("\n");
}
void printUINTRandomArray(const char * name, const void * buffer){
uint32_t storage[RNG_UINT_OUTPUT_SIZE];
memcpy(storage,buffer,RNG_UINT_OUTPUT_SIZE);
printf("%s sequence generated:\n ",name);
for( int i = 0; i < RNG_UINT_OUTPUT_SIZE;i++ )
printf("%02X",storage[i]);
printf("\n");
}
/*
function for casting uint to correct BYTE representation independently on machines endian
AMD use little endian, we need work with big endian
USE CASE --> rand_s function on AMD processors
NOT USE, BUT USEFULL
*/
void castUintToByte(uint32_t *uintArray,BYTE* output){
int i=0;
int n=4; //because uint have 32b and BYTE 8b -- 32/8 = 4
for (int 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<tlen;i++){
sum+=t[i];
// fprintf(fp, "%4.lld%s %llu %s\n", i+1, ".Time: ", t[i]," cpucycles");
if( feof(fp) ) {printf("Problem with %s file\n", name);}
// else printf("Time successfully write!\n");
}
fprintf(fp,"TOTAL: %llu cycles\nMEDIAN: %llu cpucycles\nAVERAGE: %llu cpucycles\nEXECUTED in %f s\n",sum, median(t, tlen),average(t, tlen),programRun);
}
/*############################################################
END OF AUXILIARY FUNCTIONS
##############################################################*/
/*############################################################
FUNCTIONS FOR GENERATING RANDOM VALUES VIA APIs
##############################################################*/
/*
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[RNG_BYTE_OUTPUT_SIZE];
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("winapiResults.txt","a+"); // file for storing measurments
TIMER_INIT
{TIMER_START
/*######## CALLING FUNCTIONS With APIs ##############*/
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;
}