blob: c69f6419c5dcd8412e1389ec25ec277a01968bd0 [file] [log] [blame]
/*
* Dropbear - a SSH2 server
*
* Copyright (c) 2002,2003 Matt Johnston
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. */
#include "includes.h"
#include "buffer.h"
#include "dbutil.h"
int donerandinit = 0;
/* this is used to generate unique output from the same hashpool */
unsigned int counter = 0;
#define MAX_COUNTER 1000000/* the max value for the counter, so it won't loop */
unsigned char hashpool[SHA1_HASH_SIZE];
#define INIT_SEED_SIZE 32 /* 256 bits */
static void readrand(unsigned char* buf, unsigned int buflen);
/* The basic setup is we read some data from /dev/(u)random or prngd and hash it
* into hashpool. To read data, we hash together current hashpool contents,
* and a counter. We feed more data in by hashing the current pool and new
* data into the pool.
*
* It is important to ensure that counter doesn't wrap around before we
* feed in new entropy.
*
*/
static void readrand(unsigned char* buf, unsigned int buflen) {
int readfd;
unsigned int readpos;
int readlen;
#ifdef DROPBEAR_PRNGD_SOCKET
struct sockaddr_un egdsock;
char egdcmd[2];
#endif
#ifdef DROPBEAR_RANDOM_DEV
readfd = open(DROPBEAR_RANDOM_DEV, O_RDONLY);
if (readfd < 0) {
dropbear_exit("couldn't open random device");
}
#endif
#ifdef DROPBEAR_PRNGD_SOCKET
memset((void*)&egdsock, 0x0, sizeof(egdsock));
egdsock.sun_family = AF_UNIX;
strlcpy(egdsock.sun_path, DROPBEAR_PRNGD_SOCKET,
sizeof(egdsock.sun_path));
readfd = socket(PF_UNIX, SOCK_STREAM, 0);
if (readfd < 0) {
dropbear_exit("couldn't open random device");
}
/* todo - try various common locations */
if (connect(readfd, (struct sockaddr*)&egdsock,
sizeof(struct sockaddr_un)) < 0) {
dropbear_exit("couldn't open random device");
}
if (buflen > 255)
dropbear_exit("can't request more than 255 bytes from egd");
egdcmd[0] = 0x02; /* blocking read */
egdcmd[1] = (unsigned char)buflen;
if (write(readfd, egdcmd, 2) < 0)
dropbear_exit("can't send command to egd");
#endif
/* read the actual random data */
readpos = 0;
do {
readlen = read(readfd, &buf[readpos], buflen - readpos);
if (readlen <= 0) {
if (readlen < 0 && errno == EINTR) {
continue;
}
dropbear_exit("error reading random source");
}
readpos += readlen;
} while (readpos < buflen);
close (readfd);
}
/* initialise the prng from /dev/(u)random or prngd */
void seedrandom() {
unsigned char readbuf[INIT_SEED_SIZE];
hash_state hs;
/* initialise so compilers will be happy about hashing it */
if (!donerandinit) {
m_burn(hashpool, sizeof(hashpool));
}
/* get the seed data */
readrand(readbuf, sizeof(readbuf));
/* hash in the new seed data */
sha1_init(&hs);
sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
sha1_process(&hs, (void*)readbuf, sizeof(readbuf));
sha1_done(&hs, hashpool);
counter = 0;
donerandinit = 1;
}
/* return len bytes of pseudo-random data */
void genrandom(unsigned char* buf, unsigned int len) {
hash_state hs;
unsigned char hash[SHA1_HASH_SIZE];
unsigned int copylen;
if (!donerandinit) {
dropbear_exit("seedrandom not done");
}
while (len > 0) {
sha1_init(&hs);
sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
sha1_process(&hs, (void*)&counter, sizeof(counter));
sha1_done(&hs, hash);
counter++;
if (counter > MAX_COUNTER) {
seedrandom();
}
copylen = MIN(len, SHA1_HASH_SIZE);
memcpy(buf, hash, copylen);
len -= copylen;
buf += copylen;
}
m_burn(hash, sizeof(hash));
}