/************************************************************************
* IRC - Internet Relay Chat, src/dh.c
* Copyright (C) 2000 Lucas Madar
*
* 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 1, 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: dh.c,v 1.5 2006/01/07 22:13:26 trystanscott Exp $ */
/*
* Diffie-Hellman key exchange for bahamut ircd.
* Lucas Madar <lucas@dal.net> -- 2000
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/bn.h>
#include <openssl/dh.h>
#include "memcount.h"
#define DH_HEADER
#include "dh.h"
#ifdef __OpenBSD__
#define RAND_SRC "/dev/arandom"
#else
#define RAND_SRC "/dev/random"
#endif
static int verify_is_hex(char *string)
{
int l = strlen(string);
char tmp[4] = {'\0', '\0', '\0', '\0'};
int tmpidx = 0;
if(l & 0x01) /* check if it's odd length */
{
l++;
tmp[tmpidx++] = '0'; /* pad with zero */
}
while(*string)
{
tmp[tmpidx++] = *string++;
if(tmpidx == 2)
{
char *eptr;
unsigned char x;
tmpidx = 0;
x = strtol(tmp, &eptr, 16);
if(*eptr != '\0')
return 0;
}
}
return 1;
}
int dh_hexstr_to_raw(char *string, unsigned char *hexout, int *hexlen)
{
int l = strlen(string);
char tmp[3] = {'\0', '\0', '\0'};
int tmpidx = 0;
int hexidx = 0;
if(l & 0x01) /* check if it's odd length */
{
l++;
tmp[tmpidx++] = '0'; /* pad with zero */
}
while(*string)
{
tmp[tmpidx++] = *string++;
if(tmpidx == 2)
{
char *eptr;
unsigned char x;
tmpidx = 0;
x = strtol(tmp, &eptr, 16);
if(*eptr != '\0')
return 0;
hexout[hexidx++] = (unsigned char) x;
}
}
*hexlen = hexidx;
return 1;
}
static inline void entropy_error(void)
{
printf("\nCould not generate entropy from %s:\n%s\n\n",
RAND_SRC, strerror(errno));
printf("ircd needs a %d byte random seed.\n", RAND_BYTES);
printf("You can place a file containing random data called"
" .ircd.entropy\nin the directory with your ircd.conf."
" This file must be at least %d bytes\n", RAND_BYTES);
printf("long and should be suitably random.\n");
}
static int make_entropy()
{
char randbuf[RAND_BYTES * 4];
FILE *fp;
int i;
printf("\nNo random state found, generating entropy from %s...\n",
RAND_SRC);
printf("On some systems this may take a while, and can be helped by"
" keeping the\nsystem busy, such as pounding on the keyboard.\n");
fp = fopen(RAND_SRC, "r");
if(!fp)
{
entropy_error();
return 0;
}
for(i = 0; i < (RAND_BYTES * 4); i++)
{
int cv;
cv = fgetc(fp);
if(cv == EOF)
{
if(ferror(fp))
{
entropy_error();
fclose(fp);
return 0;
}
clearerr(fp);
usleep(100);
i--;
continue;
}
randbuf[i] = cv;
if(i && (i % 64 == 0))
{
printf(" %d%% .. ", (int)(((float) i / (float) (RAND_BYTES * 4))
* 100.0));
fflush(stdout);
}
}
printf("Done.\n");
fclose(fp);
fp = fopen(".ircd.entropy", "w");
if(!fp)
{
printf("Could not open .ircd.entropy for writing: %s\n",
strerror(errno));
return 0;
}
fwrite(randbuf, RAND_BYTES * 4, 1, fp);
fclose(fp);
RAND_load_file(".ircd.entropy", -1);
return 1;
}
static int init_random()
{
int ret;
time_t now;
ret = RAND_load_file(".ircd.entropy", -1);
if(ret <= 0)
{
if(!make_entropy())
return -1;
}
else
printf("%d bytes of entropy loaded.\n", ret);
now = time(NULL);
/* this is probably not too good, but it saves just writing
the whole state back to disk with no changes. */
RAND_seed(&now, 4);
RAND_write_file(".ircd.entropy");
return 0;
}
static void create_prime()
{
char buf[PRIME_BYTES_HEX];
int i;
int bufpos = 0;
for(i = 0; i < PRIME_BYTES; i++)
{
char *x = hex_to_string[dh_prime_1024[i]];
while(*x)
buf[bufpos++] = *x++;
}
buf[bufpos] = '\0';
ircd_prime = NULL;
BN_hex2bn(&ircd_prime, buf);
ircd_generator = BN_new();
BN_set_word(ircd_generator, dh_gen_1024);
}
int dh_init()
{
ERR_load_crypto_strings();
create_prime();
if(init_random() == -1)
return -1;
return 0;
}
int dh_generate_shared(void *session, char *public_key)
{
BIGNUM *tmp;
int len;
struct session_info *si = (struct session_info *) session;
if(verify_is_hex(public_key) == 0 || !si || si->session_shared)
return 0;
tmp = NULL;
BN_hex2bn(&tmp, public_key);
if(!tmp)
return 0;
si->session_shared_length = DH_size(si->dh);
si->session_shared = (char *) malloc(DH_size(si->dh));
len = DH_compute_key((unsigned char *) si->session_shared, tmp, si->dh);
BN_free(tmp);
if(len < 0)
return 0;
si->session_shared_length = len;
return 1;
}
void *dh_start_session()
{
struct session_info *si;
si = (struct session_info *) MyMalloc(sizeof(struct session_info));
if(!si)
abort();
memset(si, 0, sizeof(struct session_info));
si->dh = DH_new();
si->dh->p = BN_dup(ircd_prime);
si->dh->g = BN_dup(ircd_generator);
if(!DH_generate_key(si->dh))
{
DH_free(si->dh);
MyFree(si);
return NULL;
}
return (void *) si;
}
void dh_end_session(void *session)
{
struct session_info *si = (struct session_info *) session;
if(si->dh)
{
DH_free(si->dh);
si->dh = NULL;
}
if(si->session_shared)
{
memset(si->session_shared, 0, si->session_shared_length);
free(si->session_shared);
si->session_shared = NULL;
}
MyFree(si);
}
char *dh_get_s_public(char *buf, int maxlen, void *session)
{
struct session_info *si = (struct session_info *) session;
char *tmp;
if(!si || !si->dh || !si->dh->pub_key)
return NULL;
tmp = BN_bn2hex(si->dh->pub_key);
if(!tmp)
return NULL;
if(strlen(tmp) + 1 > maxlen)
{
OPENSSL_free(tmp);
return NULL;
}
strcpy(buf, tmp);
OPENSSL_free(tmp);
return buf;
}
int dh_get_s_shared(char *buf, int *maxlen, void *session)
{
struct session_info *si = (struct session_info *) session;
if(!si || !si->session_shared || *maxlen < si->session_shared_length)
return 0;
*maxlen = si->session_shared_length;
memcpy(buf, si->session_shared, *maxlen);
return 1;
}
u_long
memcount_dh(MCdh *mc)
{
mc->file = __FILE__;
mc->m_dhsession_size = sizeof(struct session_info);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1