/* Copyright (c) 2000-2010, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above opyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Dirk Krause nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file dkappr.c Random data generation in applications. */ #include "dk.h" /** Inside the dkappr module. */ #define DKAPPR_C 1 #include "dkapp.h" #include "dksfc.h" #include "dksf.h" #include "dkrandc.h" #include "dkmem.h" #include "dkstr.h" #if DK_HAVE_STRING_H #include #endif #if DK_HAVE_STDLIB_H #include #endif #if DK_HAVE_OPENSSL_RAND_H #include #endif #line 69 "dkappr.ctr" /** Comma used in messages. */ static char comma[] = { "," }; /** Environment variable to inspect for random seed file name. */ static char str_randfile[] = { "RANDFILE" }; /** Environment variable to inspect for EGD socket name. */ static char str_egdsocket[] = { "EGDSOCKET" }; #if 0 static char str_egdpool[] = { "/var/run/egd-pool" }; #endif /** Preference key to enable/disable the use of seed files. */ static char key_allow_seedfile[] = { "/openssl/allow-random-seed-file" }; /** Preference key for seed file. */ static char key_seedfile[] = { "/openssl/random-seed-file" }; /** Preference key for EGD socket. */ static char key_egdsocket[] = { "/openssl/egd-socket" }; /** Default seed file name. */ static char default_seed_file[] = { "$(user.home)/.rnd" }; #if DK_HAVE_DEV_URANDOM /** Random device. */ static char str_urandom[] = { "/dev/urandom" }; #endif #if DK_HAVE_DEV_RANDOM /** Random device. */ static char str_random[] = { "/dev/random" }; #endif #if DK_HAVE_RAND && DK_HAVE_SRAND /** File name suffix for rand()/srand() seed files. */ static char str_simple_suffix[] = { ".simple" }; #endif #if DK_HAVE_INITSTATE && DK_HAVE_SETSTATE && DK_HAVE_RANDOM /** File name suffix for initstate()/setstate()/random() seed files. */ static char str_state_suffix[] = { ".isr" }; /** Internal buffer for initstate()/setstate()/random(). */ static char random_state_buffer[256]; #endif #if DK_HAVE_NRAND48 /** Suffix for nrand48() seed files. */ static char str_48_suffix[] = { ".48" }; /** 3 integers for nrand48(). */ static unsigned short xi[3]; #endif #if 0 /** Message asking for keyboard input. */ static char default_message_a[] = { "Some random input from keyboard is needed to seed the PRNG.\n" "Please hit some keys, press to finish: " }; /** Message asking for more keyboard input. */ static char default_message_b[] = { "Some more input is needed: " }; /** Warning message if keyboard echo is not disabled. */ static char default_message_c[] = { "Warning: keyboard echo is *not* disabled!" }; #endif /** PRNG names. */ static char *prng_strings[] = { "all", "openssl", "random", "rand48", "rand", NULL }; /** Get bytes from keyboard. @param a Application. @param buf Result buffer. @param sz Size of \a buf. @param eo Flag: Print newline after getting data. @return Number of bytes found. */ static size_t get_bytes_from_keyboard DK_P4(dk_app_t *,a, unsigned char *,buf, size_t, sz, int,eo) { int cc = 1; int is_first = 1; size_t back = 0; char c, inputline[512], *ptr; unsigned char uc, *bufptr; unsigned long ul, summary; int bytes_in_summary; int i; bytes_in_summary = 0; ul = summary = 0UL; bufptr = buf; while((cc) && (back < sz)) { if(is_first) { dkapp_stderr_msg_need_random_input(a); } else { dkapp_stderr_msg_need_more_random_input(a); } is_first = 0; if(fgets(inputline, sizeof(inputline), stdin)) { cc = 0; ptr = inputline; while((*ptr) && (back < sz)) { c = *(ptr++); if((c != '\n') && (c != '\r')) { cc = 1; } uc = (unsigned char)c; ul = (unsigned long)uc; ul = ul - 32UL; ul = ul % 85UL; ul &= 0x000000FFUL; summary = summary * 85UL + ul; bytes_in_summary++; if(bytes_in_summary >= 5) { for(i = 0; ((i < 4) && (back < sz)); i++) { ul = summary % 256UL; ul &= 0x000000FFUL; uc = (unsigned char)ul; summary = summary / 256UL; *(bufptr++) = uc; back++; if(back >= sz) { cc = 0; } } bytes_in_summary = 0; } } if(bytes_in_summary > 1) { for(i = 0; ((i < (bytes_in_summary - 1)) && (back < sz)); i++) { ul = summary % 256UL; ul &= 0x000000FFUL; uc = (unsigned char)ul; summary = summary / 256UL; *(bufptr++) = uc; back++; if(back >= sz) { cc = 0; } } bytes_in_summary = 0; } } else { cc = 0; } if(eo) { fputc('\n', stdout); } } return back; } /** Get seed data from keyboard hits. @param a Application. @param buf Result buffer. @param sz Size of \a buf. @return Number of bytes found. */ static size_t random_bytes_from_keyboard DK_P3(dk_app_t *,a, unsigned char *,buf, size_t, sz) { size_t back = 0; dk_echo_t echodata; int echo_off = 0; if(dksf_echo_test_tty()) { if(dksf_echo_save(&echodata)) { if(!dksf_echo_off(&echodata)) { dkapp_stderr_msg_echo_not_off(a); } else { echo_off = 1; } back = get_bytes_from_keyboard(a, buf, sz, echo_off); dksf_echo_restore(&echodata); } else { back = get_bytes_from_keyboard(a, buf, sz, 0); } } else { back = get_bytes_from_keyboard(a, buf, sz, 0); } return back; } /** Check ownership and permissions of seed file. @param a Application. @param fn File name. @return 1 on success, 0 on error. */ static int check_file_ownership_and_permissions DK_P2(dk_app_t *,a, char *,fn) { int back = 0; dk_stat_t *stp; int ft; #if !(DK_HAVE_WINDOWS_H || WIN32) int perm1, perm2; #endif #if DK_HAVE_WINDOWS_H || WIN32 stp = dkstat_open(fn); if(stp) { ft = dkstat_filetype(stp); ft &= (~(DK_FT_SYMLINK)); if(ft == DK_FT_REG) { back = 1; } else { /* Warning: Not a regular file */ dkapp_err_not_a_regular_file(a, fn); } dkstat_close(stp); } else { /* Warning: File does not exist */ dkapp_info_file_does_not_exist(a, fn); } #else long myuid, fileuid; stp = dkstat_open(fn); if(stp) { ft = dkstat_filetype(stp); ft &= (~(DK_FT_SYMLINK)); if(ft == DK_FT_REG) { fileuid = dkstat_uid(stp); if(dksf_have_getuid()) { myuid = dksf_getuid(); } else { myuid = fileuid; } if(myuid == fileuid) { perm1 = dkstat_permissions(stp); perm2 = (DK_PERM_G_READ | DK_PERM_G_WRITE | DK_PERM_G_EXECUTE); perm2 |= (DK_PERM_O_READ | DK_PERM_O_WRITE | DK_PERM_O_EXECUTE); if(!(perm1 & perm2)) { back = 1; } else { /* Warning: Wrong permissions */ dkapp_err_invalid_permissions(a, fn); } } else { /* Warning: Wrong owner */ dkapp_err_invalid_owner(a, fn); } } else { /* Warning: Not a regular file */ dkapp_err_not_a_regular_file(a, fn); } dkstat_close(stp); } else { /* Warning: File does not exist */ dkapp_info_file_does_not_exist(a, fn); } #endif return back; } #if DK_HAVE_OPENSSL_RAND_H /** Attempt to initialize OpenSSL PRNG from device. @param a Application. @param dn Device name. */ static void attempt_openssl_with_device DK_P2(dk_app_t *,a, char *,dn) { dk_stat_t *stp; int can_use_device = 0; int ft, res; stp = dkstat_open(dn); if(stp) { ft = dkstat_filetype(stp); ft &= (~(DK_FT_SYMLINK)); switch(ft) { case DK_FT_CHR: case DK_FT_BLK: { can_use_device = 1; } break; } dkstat_close(stp); } if(can_use_device) { res = RAND_load_file(dn, DK_RAND_OSSL_PRNG_SEED_BYTES); if(res > 0) { if(RAND_status() == 1) { (a->random).prng_type = DK_RAND_TYPE_OPENSSL; } } } else { /* Warning: Not a device */ dkapp_err_not_a_device(a, dn); } } #endif /** Retrieve random bytes from a random device. The data from this function can be used to seed a PRNG. @param a Application. @param dn Device name. @param b Result buffer. @param sz Size of \a b. @return Number of bytes produced. */ static size_t bytes_from_device DK_P4(dk_app_t *,a, char *,dn, unsigned char *,b, size_t,sz) { size_t back = 0; int can_use_this = 0; int ft; dk_stat_t *stp; FILE *fipo; stp = dkstat_open(dn); if(stp) { ft = dkstat_filetype(stp); dkstat_close(stp); ft &= (~(DK_FT_SYMLINK)); switch(ft) { case DK_FT_BLK: case DK_FT_CHR: { can_use_this = 1; } break; } if(can_use_this) { #if DK_HAVE_LARGEFILE64_SOURCE && DK_HAVE_FOPEN64 fipo = fopen64(dn, "rb"); #else fipo = fopen(dn, "rb"); #endif if(fipo) { back = fread((void *)b, 1, sz, fipo); fclose(fipo); } else { /* ERROR: Failed to open */ dkapp_err_fopenr(a, dn); } } } else { /* ERROR: dn does not exist */ dkapp_info_file_does_not_exist(a, dn); } return back; } int dkapp_rand_begin DK_P2(dk_app_t *,a, int,ra) { int back = 0; size_t mpl; /* MAXPATHLEN */ char *s = NULL; /* seed file name */ char *sbuffer = NULL; /* buffer for seed file name */ #if 0 char *h = NULL; /* home directory */ char *hbuffer = NULL; /* buffer for home directory */ #endif #if 0 char *e = NULL; /* EGD socket name */ #endif int allow_seed_file; /* allowed to use seed file */ unsigned char buf[256]; /* buffer for random keyboard data */ size_t buf_used = 0; /* number of bytes used in buf */ char *sa[3], *ptr; int res; char *x; FILE *fipo; unsigned int ui; /* used for srand() */ sa[0] = NULL; sa[1] = NULL; sa[2] = NULL; if((a) && (ra)) { /* initialization */ if((a->random).seed_file_name) { s = (a->random).seed_file_name; dk_delete(s); (a->random).seed_file_name = NULL; s = NULL; } (a->random).prng_type = 0; mpl = (size_t) dksf_get_maxpathlen(); allow_seed_file = 1; sbuffer = dk_new(char,mpl); if(sbuffer) { /* check whether seed file use is allowed */ if(dkapp_get_pref( a, key_allow_seedfile, sbuffer, mpl, (~(DK_APP_PREF_EXCL_SYSTEM)) )) { ptr = dkstr_start(sbuffer, NULL); if(ptr) { dkstr_chomp(ptr, NULL); if(dkstr_is_bool(ptr)) { if(!dkstr_is_on(ptr)) { allow_seed_file = 0; } } } } /* check openssl */ if(ra & DK_RAND_TYPE_OPENSSL) { #if DK_HAVE_OPENSSL_RAND_H /* seed from file */ dkapp_debug_attempt_seed_openssl(a); if(allow_seed_file) { s = NULL; if(dkapp_get_pref_env(a, key_seedfile, sbuffer, mpl, 0, str_randfile)) { ptr = dkstr_start(sbuffer, NULL); if(ptr) { dkstr_chomp(ptr, NULL); if(dkapp_transform_string_ext1(a,sbuffer,mpl,ptr,1)) { dksf_correct_fnsep(sbuffer); s = sbuffer; } } } if(!(s)) { if(dkapp_transform_string_ext1(a,sbuffer,mpl,default_seed_file,1)) { dksf_correct_fnsep(sbuffer); s = sbuffer; } } if(s) { (a->random).seed_file_name = dkstr_dup(s); dkapp_debug_attempt_seed_file(a, s); if(check_file_ownership_and_permissions(a, s)) { res = RAND_load_file(s, -1L); if(RAND_status() == 1) { (a->random).prng_type = DK_RAND_TYPE_OPENSSL; } } } } /* seed from EGD socket */ if((a->random).prng_type == 0) { if(dkapp_get_pref_env(a,key_egdsocket,sbuffer,mpl,0,str_egdsocket)) { ptr = dkstr_start(sbuffer, NULL); if(ptr) { dkstr_chomp(ptr, NULL); dkapp_debug_attempt_seed_egd(a, ptr); RAND_egd(ptr); if(RAND_status() == 1) { (a->random).prng_type = DK_RAND_TYPE_OPENSSL; } } } } /* seed from /dev/urandom, /dev/random */ #if DK_HAVE_DEV_URANDOM if((a->random).prng_type == 0) { dkapp_debug_attempt_seed_device(a, str_urandom); attempt_openssl_with_device(a, str_urandom); } #endif #if DK_HAVE_DEV_RANDOM if((a->random).prng_type == 0) { dkapp_debug_attempt_seed_device(a, str_random); attempt_openssl_with_device(a, str_random); } #endif /* seed from screen */ #ifdef WIN32 if((a->random).prng_type == 0) { dkapp_debug_attempt_seed_screen(a); RAND_screen(); if(RAND_status() == 1) { (a->random).prng_type = DK_RAND_TYPE_OPENSSL; } } #endif /* seed from keyboard */ if((a->random).prng_type == 0) { dkapp_debug_attempt_seed_kbd(a); buf_used = random_bytes_from_keyboard(a, buf, sizeof(buf)); if(buf_used >= 255) { RAND_seed((void *)buf, buf_used); if(RAND_status() == 1) { (a->random).prng_type = DK_RAND_TYPE_OPENSSL; } } } if((a->random).prng_type == 0) { dkapp_debug_failed_seed_openssl(a); } #else /* ERROR: OpenSSL not available */ dkapp_info_no_openssl_support(a); #endif } /* initstate, setstate, random */ if((a->random).prng_type == 0) { if(ra & DK_RAND_TYPE_STATE) { #if DK_HAVE_INITSTATE && DK_HAVE_SETSTATE && DK_HAVE_RANDOM dkapp_debug_attempt_seed_random(a); if(allow_seed_file) { s = NULL; if(dkapp_get_pref_env(a, key_seedfile, sbuffer, mpl, 0, str_randfile)) { ptr = dkstr_start(sbuffer, NULL); if(ptr) { dkstr_chomp(ptr, NULL); if(dkapp_transform_string_ext1(a,sbuffer,mpl,ptr,1)) { dksf_correct_fnsep(sbuffer); s = sbuffer; } } } if(!(s)) { if(dkapp_transform_string_ext1(a,sbuffer,mpl,default_seed_file,1)) { dksf_correct_fnsep(sbuffer); s = sbuffer; } } if(s) { if((strlen(s) + strlen(str_state_suffix)) < mpl) { strcat(s, str_state_suffix); if((a->random).seed_file_name) { x = (a->random).seed_file_name; dk_delete(x); (a->random).seed_file_name = NULL; } (a->random).seed_file_name = dkstr_dup(s); dkapp_debug_attempt_seed_file(a, s); if(check_file_ownership_and_permissions(a, s)) { #if DK_HAVE_LARGEFILE64_SOURCE && DK_HAVE_FOPEN64 fipo = fopen64(s, "rb"); #else fipo = fopen(s, "rb"); #endif if(fipo) { if(fread((void *)(&ui), sizeof(unsigned int), 1, fipo) == 1) { if(fread((void *)random_state_buffer, 1, 256, fipo) == 256) { initstate(ui, random_state_buffer, 256); setstate(random_state_buffer); (a->random).prng_type = DK_RAND_TYPE_STATE; } } fclose(fipo); } } } } } #if DK_HAVE_DEV_URANDOM if((a->random).prng_type == 0) { dkapp_debug_attempt_seed_device(a, str_urandom); if(bytes_from_device( a, str_urandom, (void *)(&ui), sizeof(unsigned int)) == sizeof(unsigned int)) { if(bytes_from_device(a, str_urandom, (void *)random_state_buffer, 256) == 256) { initstate(ui, random_state_buffer, 256); setstate(random_state_buffer); (a->random).prng_type = DK_RAND_TYPE_STATE; } } } #endif #if DK_HAVE_DEV_RANDOM if((a->random).prng_type == 0) { dkapp_debug_attempt_seed_device(a, str_random); if(bytes_from_device( a, str_random, (void *)(&ui), sizeof(unsigned int)) == sizeof(unsigned int)) { if(bytes_from_device(a, str_random, (void *)random_state_buffer, 256) == 256) { initstate(ui, random_state_buffer, 256); setstate(random_state_buffer); (a->random).prng_type = DK_RAND_TYPE_STATE; } } } #endif if((a->random).prng_type == 0) { dkapp_debug_attempt_seed_kbd(a); buf_used = random_bytes_from_keyboard( a, (unsigned char *)random_state_buffer, 256 ); if(buf_used == 256) { buf_used = random_bytes_from_keyboard( a, (unsigned char *)&ui, sizeof(unsigned int) ); if(buf_used == sizeof(unsigned int)) { initstate(ui, random_state_buffer, 256); setstate(random_state_buffer); (a->random).prng_type = DK_RAND_TYPE_STATE; } } } if((a->random).prng_type == 0) { /* DEBUG: Failed to seed initstate/setstate/random */ dkapp_debug_failed_seed_random(a); } #else /* DEBUG: initstate/setstate/random not available */ dkapp_debug_unavailable_random(a); #endif } } /* nrand48 */ if((a->random).prng_type == 0) { if(ra & DK_RAND_TYPE_RAND48) { #if DK_HAVE_NRAND48 dkapp_debug_attempt_seed_rand48(a); if(allow_seed_file) { s = NULL; if(dkapp_get_pref_env(a, key_seedfile, sbuffer, mpl, 0, str_randfile)) { ptr = dkstr_start(sbuffer, NULL); if(ptr) { dkstr_chomp(ptr, NULL); if(dkapp_transform_string_ext1(a,sbuffer,mpl,ptr,1)) { dksf_correct_fnsep(sbuffer); s = sbuffer; } } } if(!(s)) { if(dkapp_transform_string_ext1(a,sbuffer,mpl,default_seed_file,1)) { dksf_correct_fnsep(sbuffer); s = sbuffer; } } if(s) { if((strlen(s) + strlen(str_48_suffix)) < mpl) { strcat(s, str_48_suffix); if((a->random).seed_file_name) { x = (a->random).seed_file_name; dk_delete(x); (a->random).seed_file_name = NULL; } (a->random).seed_file_name = dkstr_dup(s); dkapp_debug_attempt_seed_file(a, s); if(check_file_ownership_and_permissions(a, s)) { #if DK_HAVE_LARGEFILE64_SOURCE && DK_HAVE_FOPEN64 fipo = fopen64(s, "rb"); #else fipo = fopen(s, "rb"); #endif if(fipo) { if(fread((void *)xi, sizeof(unsigned short), 3, fipo) == 3) { (a->random).prng_type = DK_RAND_TYPE_RAND48; } fclose(fipo); } } } } } #if DK_HAVE_DEV_URANDOM if((a->random).prng_type == 0) { dkapp_debug_attempt_seed_device(a, str_urandom); if(bytes_from_device( a, str_urandom, (void *)xi, 3*sizeof(unsigned short )) == 3*sizeof(unsigned short)) { (a->random).prng_type = DK_RAND_TYPE_RAND48; } } #endif #if DK_HAVE_DEV_RANDOM if((a->random).prng_type == 0) { dkapp_debug_attempt_seed_device(a, str_random); if(bytes_from_device( a, str_random, (void *)xi, 3*sizeof(unsigned short )) == 3*sizeof(unsigned short)) { (a->random).prng_type = DK_RAND_TYPE_RAND48; } } #endif if((a->random).prng_type == 0) { dkapp_debug_attempt_seed_kbd(a); buf_used = random_bytes_from_keyboard( a, (unsigned char *)xi, 3*sizeof(unsigned short) ); if(buf_used == 3*sizeof(unsigned short)) { (a->random).prng_type = DK_RAND_TYPE_RAND48; } } if((a->random).prng_type == 0) { /* DEBUG: Failed to seed nrand48 */ dkapp_debug_failed_seed_rand48(a); } #else /* DEBUG: nrand48 not available */ dkapp_debug_unavailable_rand48(a); #endif } } /* rand, srand */ if((a->random).prng_type == 0) { if(ra & DK_RAND_TYPE_SIMPLE) { #if DK_HAVE_RAND && DK_HAVE_SRAND dkapp_debug_attempt_seed_rand(a); if(allow_seed_file) { s = NULL; if(dkapp_get_pref_env(a, key_seedfile, sbuffer, mpl, 0, str_randfile)) { ptr = dkstr_start(sbuffer, NULL); if(ptr) { dkstr_chomp(ptr, NULL); if(dkapp_transform_string_ext1(a,sbuffer,mpl,ptr,1)) { dksf_correct_fnsep(sbuffer); s = sbuffer; } } } if(!(s)) { if(dkapp_transform_string_ext1(a,sbuffer,mpl,default_seed_file,1)) { dksf_correct_fnsep(sbuffer); s = sbuffer; } } if(s) { if((strlen(s) + strlen(str_simple_suffix)) < mpl) { strcat(s, str_simple_suffix); if((a->random).seed_file_name) { x = (a->random).seed_file_name; dk_delete(x); (a->random).seed_file_name = NULL; } (a->random).seed_file_name = dkstr_dup(s); dkapp_debug_attempt_seed_file(a, s); if(check_file_ownership_and_permissions(a, s)) { #if DK_HAVE_LARGEFILE64_SOURCE && DK_HAVE_FOPEN64 fipo = fopen64(s, "rb"); #else fipo = fopen(s, "rb"); #endif if(fipo) { if(fread((void *)(&ui), sizeof(unsigned int), 1, fipo) == 1) { srand(ui); (a->random).prng_type = DK_RAND_TYPE_SIMPLE; } fclose(fipo); } } } } } #if DK_HAVE_DEV_URANDOM if((a->random).prng_type == 0) { dkapp_debug_attempt_seed_device(a, str_urandom); if(bytes_from_device( a, str_urandom, (void *)(&ui), sizeof(unsigned int )) == sizeof(unsigned int)) { srand(ui); (a->random).prng_type = DK_RAND_TYPE_SIMPLE; } } #endif #if DK_HAVE_DEV_RANDOM if((a->random).prng_type == 0) { dkapp_debug_attempt_seed_device(a, str_random); if(bytes_from_device( a, str_random, (void *)(&ui), sizeof(unsigned int )) == sizeof(unsigned int)) { srand(ui); (a->random).prng_type = DK_RAND_TYPE_SIMPLE; } } #endif if((a->random).prng_type == 0) { dkapp_debug_attempt_seed_kbd(a); buf_used = random_bytes_from_keyboard( a, (unsigned char *)&ui, sizeof(unsigned int) ); if(buf_used == sizeof(unsigned int)) { srand(ui); (a->random).prng_type = DK_RAND_TYPE_SIMPLE; } } if((a->random).prng_type == 0) { /* DEBUG: Failed to seed rand() */ dkapp_debug_failed_seed_rand(a); } #else /* DEBUG: rand() not available */ dkapp_debug_unavailable_rand(a); #endif } } } else { /* if(sbuffer) */ dkapp_err_memory(a, 1, mpl); } /* else if(sbuffer) */ } if(sbuffer) { dk_delete(sbuffer); } back = (a->random).prng_type; if(back) { dkapp_debug_prng_seeded_successfully(a); if(ra & DK_RAND_TYPE_CRYPTO) { if(!(back & DK_RAND_TYPE_CRYPTO)) { /* Warning: The PRNG is not crypto strong */ dkapp_warn_prng_not_crypto(a); } } } else { /* ERROR: No usable PRNG found */ dkapp_err_no_prng_found(a); } return back; } #if DK_HAVE_INITSTATE && DK_HAVE_SETSTATE && DK_HAVE_RANDOM /** Retrieve bytes from the initstate()/setstate()/random() function set. @param ucb Destination buffer. @param sz Size of \a ucb. @return Number of bytes produced. */ static size_t bytes_state DK_P2(unsigned char *,ucb, size_t,sz) { unsigned char *ptr; size_t i, bytes_taken; long ul; ptr = ucb; ul = (unsigned long)random(); bytes_taken = 0; for(i = 0; i < sz; i++) { *(ptr++) = (unsigned char)(ul & 0x000000FFUL); ul = (ul >> 8); bytes_taken++; if(bytes_taken >= sizeof(unsigned long)) { bytes_taken = 0; ul = (unsigned long)random(); } } return sz; } #endif #if DK_HAVE_NRAND48 /** Retrieve bytes from the nrand48() function. @param ucb Destination buffer. @param sz Size of \a ucb. @return Number of bytes produced. */ static size_t bytes_48 DK_P2(unsigned char *,ucb, size_t,sz) { size_t back, i, position; unsigned char *ptr; long lval; ptr = ucb; lval = nrand48(xi); lval = lval >> 8; position = 0; for(i = 0; i < sz; i++) { *(ptr++) = (unsigned char)((lval) & 0xFF); if(position >= sizeof(long) - 3) { lval = nrand48(xi); lval = lval >> 8; position = 0; } else { position++; lval = lval >> 8; } } back = sz; return back; } #endif #if DK_HAVE_RAND && DK_HAVE_SRAND /** Retrieve random bytes from the simple random generator. @param ucb Destination buffer. @param sz Size of \a ucb. @return Number of bytes produced. */ static size_t bytes_simple DK_P2(unsigned char *,ucb, size_t,sz) { #if !SLOW_RAND size_t back, i, shiftbits; unsigned char *ptr; back = sz; ptr = ucb; shiftbits = (sizeof(int)-1) * 8 - 1; for(i = 0; i < back; i++) { *(ptr++) = (unsigned char)((rand() >> shiftbits) & 0xFF); } return back; #else /* as in the Linux rand() man-page */ size_t back, i; unsigned char *ptr; back = sz; ptr = ucb; for(i = 0; i < back; i++) { *(ptr++) = (unsigned char)(((int)floor(256.0*rand()/(double)RAND_MAX + 1.0)) & 0xFF); } return back; #endif } #endif size_t dkapp_rand_bytes DK_P3(dk_app_t *,a, void *,b, size_t,sz) { size_t back = 0; #if DK_HAVE_OPENSSL_RAND_H int v; #endif if((a) && (b) && (sz)) { switch((a->random).prng_type) { case DK_RAND_TYPE_OPENSSL: { #if DK_HAVE_OPENSSL_RAND_H v = RAND_bytes((unsigned char *)b, (int)sz); if(v == 1) { back = sz; } #endif } break; case DK_RAND_TYPE_STATE: { #if DK_HAVE_INITSTATE && DK_HAVE_SETSTATE && DK_HAVE_RANDOM back = bytes_state((unsigned char *)b, sz); #endif } break; case DK_RAND_TYPE_RAND48: { #if DK_HAVE_NRAND48 back = bytes_48((unsigned char *)b, sz); #endif } break; case DK_RAND_TYPE_SIMPLE: { #if DK_HAVE_RAND && DK_HAVE_SRAND back = bytes_simple((unsigned char *)b, sz); #endif } break; } } return back; } size_t dkapp_rand_bytes_non_crypto DK_P3(dk_app_t *,a, void *,b, size_t,sz) { size_t back = 0; #if DK_HAVE_OPENSSL_RAND_H int v; #endif if((a) && (b) && (sz)) { switch((a->random).prng_type) { case DK_RAND_TYPE_OPENSSL: { #if DK_HAVE_OPENSSL_RAND_H v = RAND_pseudo_bytes((unsigned char *)b, (int)sz); if(v != -1) { back = sz; } #endif } break; case DK_RAND_TYPE_STATE: { #if DK_HAVE_INITSTATE && DK_HAVE_SETSTATE && DK_HAVE_RANDOM #endif } break; case DK_RAND_TYPE_RAND48: { #if DK_HAVE_NRAND48 back = bytes_48((unsigned char *)b, sz); #endif } break; case DK_RAND_TYPE_SIMPLE: { #if DK_HAVE_RAND && DK_HAVE_SRAND back = bytes_simple((unsigned char *)b, sz); #endif } break; } } return back; } /** Ensure correct permissions for a file. The file permissions are set to 0600, the file will be created if it does not yet exist. @param a Application. @param fn File name. */ static void create_permissions DK_P2(dk_app_t *,a, char *,fn) { FILE *fipo; fipo = dkapp_fopen(a, fn, "w"); if(fipo) { fputc('\n', fipo); fclose(fipo); } dksf_chmod(fn, (DK_PERM_U_READ | DK_PERM_U_WRITE)); } int dkapp_rand_end DK_P1(dk_app_t *,a) { int back = 0; FILE *fipo; /* for saving random seed to file */ unsigned int ui; /* used to save random seed */ char *x; /* used to delete (a->random).seed_file_name */ if(a) { switch((a->random).prng_type) { case DK_RAND_TYPE_OPENSSL: { if((a->random).seed_file_name) { #if DK_HAVE_OPENSSL_RAND_H char *x; create_permissions(a, (a->random).seed_file_name); RAND_write_file((a->random).seed_file_name); x = (a->random).seed_file_name; dk_delete(x); (a->random).seed_file_name = NULL; (a->random).prng_type = 0; back = 1; #endif } } break; case DK_RAND_TYPE_STATE: { #if DK_HAVE_INITSTATE && DK_HAVE_SETSTATE && DK_HAVE_RANDOM if((a->random).seed_file_name) { unsigned int ui; create_permissions(a, (a->random).seed_file_name); if(dkapp_rand_bytes(a, (void *)(&ui), sizeof(unsigned int)) == sizeof(unsigned int)) { fipo = dkapp_fopen(a, (a->random).seed_file_name, "wb"); if(fipo) { if(fwrite((void *)(&ui), sizeof(unsigned int), 1, fipo) == 1) { if(fwrite((void *)random_state_buffer, 1, 256, fipo) == 256) { back = 1; } } fclose(fipo); } } } #endif } break; case DK_RAND_TYPE_RAND48: { #if DK_HAVE_NRAND48 if((a->random).seed_file_name) { create_permissions(a, (a->random).seed_file_name); fipo = dkapp_fopen(a, (a->random).seed_file_name, "wb"); if(fipo) { #if DK_HAVE_FILENO && DK_HAVE_FCHMOD dksf_fchmod(fileno(fipo), (DK_PERM_U_READ | DK_PERM_U_WRITE)); #endif if(fwrite((void *)xi, sizeof(unsigned short), 3, fipo) == 3) { back = 1; } fclose(fipo); } x = (a->random).seed_file_name; dk_delete(x); (a->random).seed_file_name = NULL; (a->random).prng_type = 0; } #endif } break; case DK_RAND_TYPE_SIMPLE: { #if DK_HAVE_RAND && DK_HAVE_SRAND if((a->random).seed_file_name) { create_permissions(a, (a->random).seed_file_name); fipo = dkapp_fopen(a, (a->random).seed_file_name, "wb"); if(fipo) { #if DK_HAVE_FILENO && DK_HAVE_FCHMOD dksf_fchmod(fileno(fipo), (DK_PERM_U_READ | DK_PERM_U_WRITE)); #endif if(bytes_simple((unsigned char *)(&ui), sizeof(unsigned int)) == sizeof(unsigned int)) { if(fwrite((void *)(&ui), sizeof(unsigned int), 1, fipo) == 1) { back = 1; } } fclose(fipo); } x = (a->random).seed_file_name; dk_delete(x); (a->random).seed_file_name = NULL; (a->random).prng_type = 0; } #endif } break; } } return back; } int dkapp_rand_types_from_string DK_P2(dk_app_t *,a, char *,s) { int back = 0; char *p1, *p2; int i; p1 = s; while(p1) { p2 = dkstr_next(p1, comma); p1 = dkstr_start(p1, NULL); if(p1) { dkstr_chomp(p1, NULL); i = dkstr_array_index(prng_strings, p1, 0); if(i >= 0) { switch(i) { case 0: { back |= DK_RAND_TYPE_ALL; } break; case 1: { back |= DK_RAND_TYPE_OPENSSL; } break; case 2: { back |= DK_RAND_TYPE_STATE; } break; case 3: { back |= DK_RAND_TYPE_RAND48; } break; case 4: { back |= DK_RAND_TYPE_SIMPLE; } break; } } else { dkapp_warn_unknown_prng(a, p1); } } p1 = p2; } if(back & DK_RAND_TYPE_OPENSSL) { #if !DK_HAVE_OPENSSL_RAND_H back &= (~(DK_RAND_TYPE_OPENSSL)); dkapp_debug_unavailable_openssl(a); #endif } if(back & DK_RAND_TYPE_STATE) { #if !DK_HAVE_RANDOM back &= (~(DK_RAND_TYPE_STATE)); dkapp_debug_unavailable_random(a); #endif } if(back & DK_RAND_TYPE_RAND48) { #if !DK_HAVE_NRAND48 back &= (~(DK_RAND_TYPE_RAND48)); dkapp_debug_unavailable_rand48(a); #endif } if(back & DK_RAND_TYPE_SIMPLE) { #if !DK_HAVE_RAND back &= (~(DK_RAND_TYPE_SIMPLE)); dkapp_debug_unavailable_rand(a); #endif } if(!back) { /* ERROR: No PRNG supported */ } return back; }