freeswitch/libs/libzrtp/src/zrtp_rng.c

346 lines
9.6 KiB
C

/*
* libZRTP SDK library, implements the ZRTP secure VoIP protocol.
* Copyright (c) 2006-2009 Philip R. Zimmermann. All rights reserved.
* Contact: http://philzimmermann.com
* For licensing and other legal details, see the file zrtp_legal.c.
*/
#include "zrtp.h"
#define _ZTU_ "zrtp rng"
#define MD_DIGEST_LENGTH SHA512_DIGEST_SIZE
#define MD_CTX_init(a)
#define MD_Init(a) sha512_begin(a)
#define MD_Final(a,b) sha512_end(b,a)
#define MD_Cleanup(a) zrtp_memset(a,0,sizeof(*a));
#if (ZRTP_PLATFORM == ZP_WIN32) || (ZRTP_PLATFORM == ZP_WIN64) || (ZRTP_PLATFORM == ZP_WINCE)
#include <Wincrypt.h>
HCRYPTPROV g_hCryptProv;
zrtp_status_t NtLmInitializeRNG(VOID)
{
BOOL fSuccess;
if (g_hCryptProv != 0) {
return zrtp_status_ok;
}
fSuccess = CryptAcquireContext( &g_hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT);
return (TRUE == fSuccess) ? zrtp_status_ok : zrtp_status_fail;
}
void NtLmCleanupRNG(VOID)
{
if (g_hCryptProv) {
CryptReleaseContext(g_hCryptProv, 0);
g_hCryptProv = 0;
}
}
int zrtp_add_system_state(zrtp_global_t* zrtp, MD_CTX *ctx)
{
uint8_t buffer[64];
if(!CryptGenRandom(g_hCryptProv, sizeof(buffer), buffer)) {
ZRTP_LOG(1,(_ZTU_,"\tERROR! Error during CryptGenRandom.\n"));
return 0;
}
MD_Update(ctx, buffer, sizeof(buffer));
ZeroMemory((PVOID)buffer, sizeof(buffer));
return sizeof(buffer);
}
#elif (ZRTP_PLATFORM == ZP_WIN32_KERNEL)
#include <Ndis.h>
/*----------------------------------------------------------------------------*/
int zrtp_add_system_state(zrtp_global_t* zrtp, MD_CTX *ctx)
{
LARGE_INTEGER li1;
LARGE_INTEGER li2;
ULONG ul1;
ULONG ul2;
ULONGLONG ull;
PKTHREAD thread;
static int tsc_ok = 1;
/*
* WARNING!
* Of course it's not a real size of entropy added to the context. It's very
* difficult to compute the size of real random data and estimate its quality.
* This value means: size of maximum possible random data which this function can provide.
*/
static int entropy_length = sizeof(LARGE_INTEGER)*2 + sizeof(PKTHREAD) +
sizeof(ULONG)*2 + sizeof(LARGE_INTEGER)*2 + sizeof(ULONG)*2;
li2 = KeQueryPerformanceCounter(&li1);
MD_Update(ctx, &li1, sizeof(LARGE_INTEGER));
MD_Update(ctx, &li2, sizeof(LARGE_INTEGER));
ull = KeQueryInterruptTime();
MD_Update(ctx, &ull, sizeof(ULONGLONG));
thread = KeGetCurrentThread();
MD_Update(ctx, &thread, sizeof(PKTHREAD));
ul2 = KeQueryRuntimeThread(thread, &ul1);
MD_Update(ctx, &ul1, sizeof(ULONG));
MD_Update(ctx, &ul2, sizeof(ULONG));
KeQuerySystemTime(&li1);
MD_Update(ctx, &li1, sizeof(LARGE_INTEGER));
KeQueryTickCount(&li1);
MD_Update(ctx, &li1, sizeof(LARGE_INTEGER));
if (tsc_ok) {
__try {
ull = _RDTSC();
MD_Update(ctx, &ull, sizeof(ULONGLONG));
} __except(EXCEPTION_EXECUTE_HANDLER) {
tsc_ok = 0;
}
}
return entropy_length;
}
#elif ((ZRTP_PLATFORM == ZP_SYMBIAN))
/*
* WARNING!
* This is just a stub to let you start with something little bit better then zero.
* We have no possibility to implement entropy collection in this abstract cross-platform
* application. This function MUST NOT be used as example in real applications. For more
* information read \ref RNG in developers guide
*
* To add real entropy - capture random data from microphone and camera.
*/
extern uint32_t zrtp_symbian_kernel_random();
extern uint32_t zrtp_sum_of_pid_and_number_of_poccesses();
extern uint64_t zrtp_get_system_time_crazy();
extern unsigned int zrtp_get_pid();
extern uint32_t zrtp_get_availible_heap();
int zrtp_add_system_state(zrtp_global_t* zrtp, MD_CTX *ctx) {
uint64_t sysdate;
unsigned int pid;
uint32_t crazy_pid_sum;
uint32_t heap_size;
static int entropy_length = sizeof(sysdate) + sizeof(pid)
+ sizeof(crazy_pid_sum) + sizeof(heap_size);
sysdate = zrtp_get_system_time_crazy();
MD_Update(ctx,&sysdate,sizeof(sysdate));
pid = zrtp_get_pid();
MD_Update(ctx,&pid,sizeof(pid));
crazy_pid_sum = zrtp_sum_of_pid_and_number_of_poccesses();
MD_Update(ctx,&crazy_pid_sum,sizeof(crazy_pid_sum));
heap_size = zrtp_get_availible_heap();
MD_Update(ctx,&heap_size,sizeof(heap_size));
return entropy_length;
}
#elif ( (ZRTP_PLATFORM == ZP_LINUX) || (ZRTP_PLATFORM == ZP_DARWIN) || (ZRTP_PLATFORM == ZP_BSD) || (ZRTP_PLATFORM == ZP_ANDROID) )
#if ZRTP_HAVE_STDIO_H == 1
# include <stdio.h>
#else
# error "Used environment dosn't have <stdio.h> - zrtp_rng.c can't be build."
#endif
/*----------------------------------------------------------------------------*/
int zrtp_add_system_state(zrtp_global_t* zrtp, MD_CTX *ctx)
{
uint8_t buffer[64];
size_t bytes_read = 0;
static size_t length= sizeof(buffer);
FILE *fp = NULL;
fp = fopen("/dev/urandom", "rb");
if (!fp) {
ZRTP_LOG(1,(_ZTU_,"\tERROR! can't get access to /dev/urandom - trying /dev/random.\n"));
fp = fopen("/dev/random", "rb");
}
if (fp) {
int number_of_retries = 1024;
while ((bytes_read < length) && (number_of_retries-- > 0)) {
setbuf(fp, NULL); /* Otherwise fread() tries to read() 4096 bytes or other default value */
bytes_read += fread(buffer+bytes_read, 1, length-bytes_read, fp);
}
if (0 != fclose(fp)) {
ZRTP_LOG(1,(_ZTU_,"\tERROR! unable to cloas /dev/random\n"));
}
} else {
ZRTP_LOG(1,(_ZTU_,"\tERROR! RNG Can't open /dev/random\n"));
}
if (bytes_read < length) {
ZRTP_LOG(1,(_ZTU_,"\tERROR! can't read random string! Current session have to be closed.\n"));
return -1;
}
MD_Update(ctx, buffer, length);
zrtp_memset(buffer, 0, sizeof(buffer));
return bytes_read;
}
#endif
/*----------------------------------------------------------------------------*/
zrtp_status_t zrtp_init_rng(zrtp_global_t* zrtp)
{
if (!zrtp->rand_initialized) {
zrtp_mutex_init(&zrtp->rng_protector);
MD_Init(&zrtp->rand_ctx);
#if (ZRTP_PLATFORM == ZP_WIN32) || (ZRTP_PLATFORM == ZP_WIN64) || (ZRTP_PLATFORM == ZP_WINCE)
if (zrtp_status_ok != NtLmInitializeRNG()) {
ZRTP_LOG(1,(_ZTU_,"\tERROR! during CryptAcquireContext!\n"));
return zrtp_status_fail;
}
#endif
zrtp->rand_initialized = 1;
}
return zrtp_status_ok;
}
void zrtp_down_rng(zrtp_global_t* zrtp)
{
if (zrtp->rand_initialized) {
zrtp_mutex_destroy(zrtp->rng_protector);
#if (ZRTP_PLATFORM == ZP_WIN32) || (ZRTP_PLATFORM == ZP_WIN64) || (ZRTP_PLATFORM == ZP_WINCE)
NtLmCleanupRNG();
#endif
zrtp->rand_initialized = 0;
}
}
/*
* Call this to add entropy to the system from the given buffer,
* and also from the system state. It's OK to pass a null buffer
* with a length of zero, then we will just use the system entropy.
*/
/*----------------------------------------------------------------------------*/
int zrtp_entropy_add(zrtp_global_t* zrtp, const unsigned char *buffer, uint32_t length)
{
if (buffer && length) {
MD_Update(&zrtp->rand_ctx, buffer, length);
}
return zrtp_add_system_state(zrtp, &zrtp->rand_ctx);
}
/*
* Random bits are produced as follows.
* First stir new entropy into the random state (zrtp->rand_ctx).
* Then make a copy of the random context and finalize it.
* Use the digest to seed an AES-256 context and, if space remains, to
* initialize a counter.
* Then encrypt the counter with the AES-256 context, incrementing it
* per block, until we have produced the desired quantity of data.
*/
/*----------------------------------------------------------------------------*/
int zrtp_randstr(zrtp_global_t* zrtp, unsigned char *buffer, uint32_t length)
{
//TODO: replace bg_aes_xxx() with our own block cipher component.
//TODO: Do the same with the hash functions.
aes_encrypt_ctx aes_ctx;
MD_CTX rand_ctx2;
unsigned char md[MD_DIGEST_LENGTH];
unsigned char ctr[AES_BLOCK_SIZE];
unsigned char rdata[AES_BLOCK_SIZE];
uint32_t generated = length;
/*
* In few cases we need to gerate random value before initializing libzrtp engine.
* Following trick makes it possible.
*/
if (!zrtp->rand_initialized) {
if (zrtp_status_ok != zrtp_init_rng(zrtp)) {
return -1;
}
}
zrtp_mutex_lock(zrtp->rng_protector);
/*
* Add entropy from system state
* We will include whatever happens to be in the buffer, it can't hurt
*/
if ( 0 > zrtp_entropy_add(zrtp, buffer, length) ) {
zrtp_mutex_unlock(zrtp->rng_protector);
return -1;
}
/* Copy the zrtp->rand_ctx and finalize it into the md buffer */
rand_ctx2 = zrtp->rand_ctx;
MD_Final(&rand_ctx2, md);
zrtp_mutex_unlock(zrtp->rng_protector);
/* Key an AES context from this buffer */
zrtp_bg_aes_encrypt_key256(md, &aes_ctx);
/* Initialize counter, using excess from md if available */
zrtp_memset (ctr, 0, sizeof(ctr));
if (MD_DIGEST_LENGTH > (256/8)) {
uint32_t ctrbytes = MD_DIGEST_LENGTH - (256/8);
if (ctrbytes > AES_BLOCK_SIZE)
ctrbytes = AES_BLOCK_SIZE;
zrtp_memcpy(ctr + sizeof(ctr) - ctrbytes, md + (256/8), ctrbytes);
}
/* Encrypt counter, copy to destination buffer, increment counter */
while (length)
{
unsigned char *ctrptr;
uint32_t copied;
zrtp_bg_aes_encrypt(ctr, rdata, &aes_ctx);
copied = (sizeof(rdata) < length) ? sizeof(rdata) : length;
zrtp_memcpy (buffer, rdata, copied);
buffer += copied;
length -= copied;
/* Increment counter */
ctrptr = ctr + sizeof(ctr) - 1;
while (ctrptr >= ctr) {
if ((*ctrptr-- += 1) != 0) {
break;
}
}
}
/* Done! Cleanup and exit */
MD_Cleanup (&rand_ctx2);
MD_Cleanup (md);
MD_Cleanup (&aes_ctx);
MD_Cleanup (ctr);
MD_Cleanup (rdata);
return generated;
}