Synopsis: OpenSSL PRNG weakness (up to 0.9.6a)
NetBSD versions: 1.5, 1.5.1
Thanks to: Jun-ichiro itojun Hagino
Reported in NetBSD Security Advisory: NetBSD-SA2001-013

Index: md_rand.c
===================================================================
RCS file: /cvsroot/basesrc/crypto/dist/openssl/crypto/rand/md_rand.c,v
retrieving revision 1.1.1.1.2.1
retrieving revision 1.1.1.1.2.2
diff -c -p -r1.1.1.1.2.1 -r1.1.1.1.2.2
*** md_rand.c	2000/07/17 05:59:20	1.1.1.1.2.1
--- md_rand.c	2001/07/29 04:24:34	1.1.1.1.2.2
*************** static void ssleay_rand_initialize(void)
*** 412,417 ****
--- 412,418 ----
  static int ssleay_rand_bytes(unsigned char *buf, int num)
  	{
  	int i,j,k,st_num,st_idx;
+ 	int num_ceil;
  	int ok;
  	long md_c[2];
  	unsigned char local_md[MD_DIGEST_LENGTH];
*************** static int ssleay_rand_bytes(unsigned ch
*** 431,436 ****
--- 432,443 ----
  		}
  #endif
  
+ 	if (num <= 0)
+ 		return 1;
+ 	
+ 	/* round upwards to multiple of MD_DIGEST_LENGTH/2 */
+ 	num_ceil = (1 + (num-1)/(MD_DIGEST_LENGTH/2)) * (MD_DIGEST_LENGTH/2);
+ 
  	/*
  	 * (Based on the rand(3) manpage:)
  	 *
*************** static int ssleay_rand_bytes(unsigned ch
*** 476,486 ****
  	md_c[1] = md_count[1];
  	memcpy(local_md, md, sizeof md);
  
! 	state_index+=num;
  	if (state_index > state_num)
  		state_index %= state_num;
  
! 	/* state[st_idx], ..., state[(st_idx + num - 1) % st_num]
  	 * are now ours (but other threads may use them too) */
  
  	md_count[0] += 1;
--- 483,493 ----
  	md_c[1] = md_count[1];
  	memcpy(local_md, md, sizeof md);
  
! 	state_index+=num_ceil;
  	if (state_index > state_num)
  		state_index %= state_num;
  
! 	/* state[st_idx], ..., state[(st_idx + num_ceil - 1) % st_num]
  	 * are now ours (but other threads may use them too) */
  
  	md_count[0] += 1;
*************** static int ssleay_rand_bytes(unsigned ch
*** 488,493 ****
--- 495,501 ----
  
  	while (num > 0)
  		{
+ 		/* num_ceil -= MD_DIGEST_LENGTH/2 */
  		j=(num >= MD_DIGEST_LENGTH/2)?MD_DIGEST_LENGTH/2:num;
  		num-=j;
  		MD_Init(&m);
*************** static int ssleay_rand_bytes(unsigned ch
*** 498,524 ****
  			curr_pid = 0;
  			}
  #endif
! 		MD_Update(&m,&(local_md[MD_DIGEST_LENGTH/2]),MD_DIGEST_LENGTH/2);
  		MD_Update(&m,(unsigned char *)&(md_c[0]),sizeof(md_c));
  #ifndef PURIFY
  		MD_Update(&m,buf,j); /* purify complains */
  #endif
! 		k=(st_idx+j)-st_num;
  		if (k > 0)
  			{
! 			MD_Update(&m,&(state[st_idx]),j-k);
  			MD_Update(&m,&(state[0]),k);
  			}
  		else
! 			MD_Update(&m,&(state[st_idx]),j);
  		MD_Final(local_md,&m);
  
! 		for (i=0; i<j; i++)
  			{
  			state[st_idx++]^=local_md[i]; /* may compete with other threads */
- 			*(buf++)=local_md[i+MD_DIGEST_LENGTH/2];
  			if (st_idx >= st_num)
  				st_idx=0;
  			}
  		}
  
--- 506,533 ----
  			curr_pid = 0;
  			}
  #endif
! 		MD_Update(&m,local_md,MD_DIGEST_LENGTH);
  		MD_Update(&m,(unsigned char *)&(md_c[0]),sizeof(md_c));
  #ifndef PURIFY
  		MD_Update(&m,buf,j); /* purify complains */
  #endif
! 		k=(st_idx+MD_DIGEST_LENGTH/2)-st_num;
  		if (k > 0)
  			{
! 			MD_Update(&m,&(state[st_idx]),MD_DIGEST_LENGTH/2-k);
  			MD_Update(&m,&(state[0]),k);
  			}
  		else
! 			MD_Update(&m,&(state[st_idx]),MD_DIGEST_LENGTH/2);
  		MD_Final(local_md,&m);
  
! 		for (i=0; i<MD_DIGEST_LENGTH/2; i++)
  			{
  			state[st_idx++]^=local_md[i]; /* may compete with other threads */
  			if (st_idx >= st_num)
  				st_idx=0;
+ 			if (i < j)
+ 				*(buf++)=local_md[i+MD_DIGEST_LENGTH/2];
  			}
  		}
  
