BachelorWork/appendixes/BC_ZK/winAPIprng.c

391 lines
14 KiB
C
Raw Permalink Normal View History

2021-05-24 20:44:55 +00:00
/*
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
2021-05-27 22:53:37 +00:00
PLUS rand_s API for PRNG
2021-05-24 20:44:55 +00:00
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>
2021-05-27 22:53:37 +00:00
#define _CRT_RAND_S
#include <stdlib.h>
2021-05-24 20:44:55 +00:00
#include <stdint.h>
#include <windows.h>
#include <ntsecapi.h>
#include <ntstatus.h>
/*
2021-05-27 22:53:37 +00:00
ULONG_MAX = 4096 MB --> 4. meranie (not possible with rand_s)
2021-05-24 20:44:55 +00:00
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 ..
2021-05-27 22:53:37 +00:00
#define RNG_PER_CYKLE_BITS 16*1024*1024*8
//number of calls --> 16gb now
2021-05-24 20:44:55 +00:00
#define RNG_CYCLES 1024
// byte size of output
2021-05-27 22:53:37 +00:00
#define RNG_BYTE_OUTPUT_SIZE RNG_PER_CYKLE_BITS/8
2021-05-24 20:44:55 +00:00
//uint size of output
2021-05-27 22:53:37 +00:00
#define RNG_UINT_OUTPUT_SIZE RNG_PER_CYKLE_BITS/32
2021-05-24 20:44:55 +00:00
//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);
2021-05-27 22:53:37 +00:00
for( int i = 0; i < RNG_BYTE_OUTPUT_SIZE;i++ )
printf("%02X",storage[i]);
printf("\n");
2021-05-24 20:44:55 +00:00
}
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);
2021-05-27 22:53:37 +00:00
for( int i = 0; i < RNG_UINT_OUTPUT_SIZE;i++ )
printf("%02X",storage[i]);
printf("\n");
2021-05-24 20:44:55 +00:00
}
/*
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
*/
void castUintToByte(uint32_t *uintArray,BYTE* output){
int i=0;
int n=4; //because uint have 32b and BYTE 8b -- 32/8 = 4
2021-05-27 22:53:37 +00:00
for (uint32_t j = 0; j < RNG_UINT_OUTPUT_SIZE; j++){
for(; n-->0; i++)
output[i]=(uintArray[j]>>(n*8))& 0xFF;
n=4;
2021-05-24 20:44:55 +00:00
}
}
//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++){
2021-05-27 22:53:37 +00:00
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");}
2021-05-24 20:44:55 +00:00
}
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
##############################################################*/
2021-05-27 22:53:37 +00:00
/*
and insert 32bit random value into
return errno_t value, on success equal 0
*/
/*
rand_s
this function for generating random bits needs unsigned int variable for store output
and insert 32bit random value into
return errno_t value, on success equal 0
*/
int randS(BYTE * output,FILE * timeFile){
FILE * storeFile=fopen("Rand_s.bin","ab+");
uint32_t* number=NULL; // uint32_t --> 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;
}
2021-05-24 20:44:55 +00:00
/*
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
2021-05-27 22:53:37 +00:00
{TIMER_START
2021-05-24 20:44:55 +00:00
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);
}
2021-05-27 22:53:37 +00:00
TIMER_STOP}
2021-05-24 20:44:55 +00:00
store_Time_Data("CRYPTGENRANDOM:",time,RNG_CYCLES,timeFile, elapsedTime);
fclose(storeFile);
if (check==RNG_CYCLES){
check=CryptReleaseContext(hCryptProv,0);
if (check){
return 0;
2021-05-27 22:53:37 +00:00
}
2021-05-24 20:44:55 +00:00
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++ ){
2021-05-27 22:53:37 +00:00
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}
2021-05-24 20:44:55 +00:00
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
2021-05-27 22:53:37 +00:00
FILE * timeFile=fopen("winapiResult.txt","a+"); // file for storing measurments
2021-05-24 20:44:55 +00:00
TIMER_INIT
{TIMER_START
/*######## CALLING FUNCTIONS With APIs ##############*/
2021-05-27 22:53:37 +00:00
randS(pbData,timeFile);
cryptgenrandom(pbData,timeFile);
bcrypt(pbData,timeFile);
rtlgenrandom(pbData,timeFile);
2021-05-24 20:44:55 +00:00
/*###################### END ########################*/
TIMER_STOP}
printf("Whole program executed in: %f sec\n",elapsedTime);
fclose(timeFile);
if (pbData) free(pbData);
printf("COMPLETE!\n");
return 0;
}