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

223 lines
7.7 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
Compiling: gcc ./amdINC/secrng.c amdSPRNG.c -Wall -g -I./amdINC -mrdrnd -mrdseed -o amdSPRNG ,
Version: 1.1, 19.05.2021.
Description:
This program is only for educational purposes.
Program use 4 AMD APIs (defined in secrng.h) to generate random bytes:
is_RDRAND_supported();
is_RDSEED_supported();
get_rdseed_bytes_arr(unsigned char *rng_arr, unsigned int N, unsigned int retry_count)
get_rdrand_bytes_arr(unsigned char *rng_arr, unsigned int N, unsigned int retry_count)
Usage: In macro N define how much random bytes you want per one generating.
CYCLES then defined how much times you want to repeat generating process
Program then generate output according APIs and save values to .bin files with name by current RD name
*/
#define __USE_MINGW_ANSI_STDIO
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <windows.h>
#include "secrng.h"
// count of API calls
#define CYCLES 1024
// output size per API call in Bytes
#define N 16384
// ULONG_MAX should be same as UINT_MAX -- 4GB
//N x CYCLES = output size in bytes
//33554432 B == 32 MB
//16777216 B == 16 MB
//16384 B == 16kB
//retry count if rng fails
#define NT 15
//Macros For time measurments
#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=(float)(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];
}
// storing random data from generating
void store_Data(FILE * fp, unsigned char * pbBuffer){
fwrite(pbBuffer,sizeof(unsigned char),N,fp);
if( feof(fp) ) {printf("Problem with %s file\n", (char*)fp);}
}
//storing time data from 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);}
}
fprintf(fp,"TOTAL: %llu\nMEDIAN: %llu cpucycles\nAVERAGE: %llu cpucycles\nEXECUTED in %f s\n",sum, median(t, tlen),average(t, tlen),programRun);
}
int rdseed(FILE * timeFile,unsigned char* rng_chararr){
FILE * RDSEED=fopen("RDSEED.bin","ab+");
int ret;
//Get random bytes of a given size
if (!rng_chararr){rng_chararr = (unsigned char*) malloc(sizeof(unsigned char) * N);}
uint64_t time[CYCLES],tick;
TIMER_INIT
{TIMER_START
for (int i = 0; i < CYCLES; i++){
tick = cpucyclesS();
ret = get_rdseed_bytes_arr(rng_chararr, N, NT);
time[i]=cpucyclesE() - tick;
if (ret != SECRNG_SUCCESS){
printf("Failure in %d. cycle -- RDRAND! \n",i);
}
store_Data(RDSEED,rng_chararr); //dont use for better time test}
}
TIMER_STOP}
store_Time_Data("RDSEED:",time,CYCLES,timeFile, elapsedTime);
fclose(RDSEED);
return ret;
}
int rdrand(FILE * timeFile,unsigned char* rng_chararr){
//Get random bytes of given size
FILE * RDRAND=fopen("RDRAND.bin","ab+");
int ret;
if (!rng_chararr){rng_chararr = (unsigned char*) malloc(sizeof(unsigned char) * N);}
uint64_t time[CYCLES], tick;
TIMER_INIT
{TIMER_START
for (int i = 0; i < CYCLES; i++){
tick = cpucyclesS();
ret = get_rdrand_bytes_arr(rng_chararr, N, NT);
time[i] = cpucyclesE() - tick;
if (ret != SECRNG_SUCCESS){
printf("Failure in %d. cycle -- RDRAND! \n",i);
}
store_Data(RDRAND,rng_chararr); //dont use for better time test}
}
TIMER_STOP}
store_Time_Data("RDRAND:",time,CYCLES,timeFile, elapsedTime);
fclose(RDRAND);
return ret;
}
int main(){
//RDTimeExecution
FILE * timeFile=fopen("rdResults.txt","a+");
int ret = 0;
unsigned char* rng_chararr = NULL;
ret = is_RDSEED_supported();
if (ret == SECRNG_SUPPORTED)
ret= rdseed(timeFile,rng_chararr);
else
printf("No support for RDSEED!\n");
ret = is_RDRAND_supported();
if (ret == SECRNG_SUPPORTED)
ret= rdrand(timeFile,rng_chararr);
else
printf("No support for RDRAND!\n");
if (rng_chararr) free(rng_chararr);
fclose(timeFile);
printf("PROGRAM COMPLETE!\n");
return ret;
}