This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/wwlib/srandom.cpp

197 lines
No EOL
4.9 KiB
C++

/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** 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 <http://www.gnu.org/licenses/>.
*/
//
// SecureRandomClass - Generate random values
//
#pragma warning(disable : 4514) // unreferenced inline function removed....
#include "srandom.h"
#include <stdlib.h>
#include <stdio.h>
#ifdef _UNIX
#include "osdep.h"
#else
#include "win.h"
#include <process.h>
#endif
#include <time.h>
#include <assert.h>
#include "sha.h"
// Static class variables
unsigned char SecureRandomClass::Seeds[SecureRandomClass::SeedLength];
bool SecureRandomClass::Initialized=false;
unsigned int SecureRandomClass::RandomCache[SecureRandomClass::SHADigestBytes / sizeof(unsigned int)];
int SecureRandomClass::RandomCacheEntries=0;
unsigned int SecureRandomClass::Counter=0;
Random3Class SecureRandomClass::RandomHelper;
SecureRandomClass::SecureRandomClass()
{
if (Initialized == false)
{
Generate_Seed();
Initialized=true;
}
}
SecureRandomClass::~SecureRandomClass()
{
}
//
// Add seed values to our pool of randomness
//
void SecureRandomClass::Add_Seeds(unsigned char *values, int length)
{
for (int i=0; i<length; i++)
{
Seeds[0]^=values[i];
// Rotate the seeds to the left
unsigned char uctemp=Seeds[SeedLength-1];
for (int j=SeedLength-1; j>=1; j--)
Seeds[j]=Seeds[j-1];
Seeds[0]=uctemp;
}
// We have a better seed pool now so trigger new random values
RandomCacheEntries=0;
}
//
// Get a 32bit random value
//
unsigned long SecureRandomClass::Randval(void)
{
if (RandomCacheEntries == 0)
{
SHAEngine sha;
char digest[SHADigestBytes]; // SHA produces a 20 byte hash
sha.Hash(Seeds, SeedLength);
sha.Result(digest);
memcpy(RandomCache, digest, SHADigestBytes);
RandomCacheEntries=(SHADigestBytes / sizeof(unsigned int));
unsigned int *int_seeds=(unsigned int *)Seeds;
int_seeds[0]^=Counter; // remove the last counter (double xor)
int_seeds[0]^=(Counter+1); // put the new counter in place
int_seeds[(SeedLength/sizeof(int))-1]^=Counter; // remove the last counter (double xor)
int_seeds[(SeedLength/sizeof(int))-1]^=(Counter+1); // put the new counter in place
Counter++; // increment counter
}
unsigned long retval=RandomCache[--RandomCacheEntries];
// SHA doesn't have the best distribution properties in the world
// We'll XOR the result with the output of another random number
unsigned long helperval=RandomHelper();
retval^=helperval;
return(retval);
}
/////////////////////////////// Private Methods ///////////////////////////////////////
//
// Seed the random number generator.
// The seed is what makes each run of random numbers unique. If an observer
// can guess your seed they can predict your random numbers.
//
// Note the use of XORs everywhere. The XOR of a good random number and a bad random
// number is still a good random number.
//
// Caution: Under windows this isn't nearly as safe as under UNIX!
//
void SecureRandomClass::Generate_Seed(void)
{
int i;
// Start with some garbage values
memset(Seeds, 0xAA, SeedLength);
unsigned int *int_seeds=(unsigned int *)Seeds;
int int_seed_length=SeedLength/sizeof(unsigned int);
#ifdef _UNIX
//
// On UNIX we've already got a great random number souce.
// This should be used only for a seed since it's slow.
//
FILE *in=fopen("/dev/random","r");
if (in)
{
for (i=0; i<SeedLength; i++)
Seeds[i]^=fgetc(in);
fclose(in);
}
else
assert(0);
#else
//
// Get free drive space
//
DWORD spc, bps, nfc, tnc; // various drive attributes (we don't care what they mean)
GetDiskFreeSpace(NULL, &spc, &bps, &nfc, &tnc);
int_seeds[0]^=spc;
int_seeds[1 % int_seed_length]^=bps;
int_seeds[2 % int_seed_length]^=nfc;
int_seeds[3 % int_seed_length]^=tnc;
//
// Get computer & user name
//
char comp_name[128];
char user_name[128];
DWORD comp_len=128;
DWORD name_len=128;
GetComputerName(comp_name, &comp_len);
GetUserName(user_name, &name_len);
for (i=0; i<128; i++)
{
// Offset in case user_name == comp_name
Seeds[(i+0) % SeedLength]^=comp_name[i];
Seeds[(i+2) % SeedLength]^=user_name[i];
}
#endif
for (i=0; i<int_seed_length; i++)
{
if ((i % 4) == 0)
int_seeds[i]^=time(NULL);
else if ((i % 4) == 1)
int_seeds[i]^=getpid();
else if ((i % 4) == 2)
int_seeds[i]^=GetTickCount();
else if ((i % 4) == 3)
int_seeds[i]^=i;
}
}