2  * Copyright (c) 1999, 2000-2001 Apple Computer, Inc. All rights reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   6  * The contents of this file constitute Original Code as defined in and 
   7  * are subject to the Apple Public Source License Version 1.1 (the 
   8  * "License").  You may not use this file except in compliance with the 
   9  * License.  Please obtain a copy of the License at 
  10  * http://www.apple.com/publicsource and read it before using this file. 
  12  * This Original Code and all software distributed under the License are 
  13  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  14  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  15  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
  16  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the 
  17  * License for the specific language governing rights and limitations 
  20  * @APPLE_LICENSE_HEADER_END@ 
  26         Contains:       Core routines for the Counterpane Yarrow PRNG. 
  28         Written by:     Counterpane, Inc.  
  30         Copyright: (c) 2000 by Apple Computer, Inc., all rights reserved. 
  32         Change History (most recent first): 
  34                 02/10/99        dpm             Created, based on Counterpane source. 
  40         Core routines for the Counterpane PRNG 
  42 #include "userdefines.h" 
  43 #include "assertverify.h" 
  44 #include "dev/random/YarrowCoreLib/include/yarrowUtils.h" 
  46 #if             defined(macintosh) || defined(__APPLE__) 
  47 /* FIXME - this file needs to be in a platform-independent place */ 
  49 #include <dev/random/YarrowCoreLib/include/macos_defs.h> 
  52 #endif  /* macintosh */ 
  55 #include "entropysources.h" 
  57 #include "dev/random/YarrowCoreLib/include/yarrow.h" 
  62 #define _MAX(a,b) (((a)>(b))?(a):(b)) 
  63 #define _MIN(a,b) (((a)<(b))?(a):(b)) 
  65 #if             defined(macintosh) || defined(__APPLE__) 
  67  * No mutexes in this module for Macintosh/OSX. We handle the 
  68  * required locking elsewhere.  
  70 #define MUTEX_ENABLE    0 
  72 #include <string.h>             /* memcpy, etc. */ 
  73 #if             TARGET_API_MAC_OSX 
  74         #include <sys/time.h>           /* for timespec */ 
  75 #elif   TARGET_API_MAC_CARBON 
  76         #include <Timer.h>                              /* Microseconds */ 
  81         #error Unknown TARGET_API 
  82 #endif  /* TARGET_API */ 
  84 #define MUTEX_ENABLE    1 
  85 #endif  /* macintosh */ 
  88 static HANDLE Statmutex 
= NULL
; 
  89 static DWORD mutexCreatorId 
= 0; 
  93 #pragma mark * * * Static Utility functions * * *  
  95 /* All error checking should be done in the function that calls these */ 
  98  * out := SHA1(IV | out)  
 101 prng_do_SHA1(GEN_CTX 
*ctx
)  
 106         SHA1Update(&sha
,ctx
->IV
,20); 
 107         SHA1Update(&sha
,ctx
->out
,20); 
 108         SHA1Final(ctx
->out
,&sha
); 
 116  * Called from init, prngForceReseed(), and prngOutput() 
 117  * as anti-backtracking mechanism. 
 120 prng_make_new_state(GEN_CTX 
*ctx
,BYTE 
*newState
)  
 124         memcpy(ctx
->IV
,newState
,20); 
 126         SHA1Update(&sha
,ctx
->IV
,20); 
 127         SHA1Final(ctx
->out
,&sha
); 
 135 /* Initialize the secret state with a slow poll */ 
 136 /* Currently only called from prngInitialize */ 
 138 #define SPLEN 65536  /* 64K */ 
 141 prng_slow_init(PRNG 
*p
) 
 142 /* This fails silently and must be fixed. */ 
 144         SHA1_CTX
* ctx 
= NULL
; 
 145         MMPTR mmctx 
= MM_NULL
; 
 147         MMPTR mmbigbuf 
= MM_NULL
; 
 149         MMPTR mmbuf 
= MM_NULL
; 
 152         mmbigbuf 
= mmMalloc(SPLEN
); 
 153         if(mmbigbuf 
== MM_NULL
) {goto cleanup_slow_init
;} 
 154         bigbuf 
= (BYTE
*)mmGetPtr(mmbigbuf
); 
 156         mmbuf 
= mmMalloc(20); 
 157         if(mmbuf 
== MM_NULL
) {goto cleanup_slow_init
;} 
 158         buf 
= (BYTE
*)mmGetPtr(mmbuf
); 
 160         mmctx 
= mmMalloc(sizeof(SHA1_CTX
)); 
 161         if(mmctx 
== MM_NULL
) {goto cleanup_slow_init
;} 
 162         ctx 
= (SHA1_CTX
*)mmGetPtr(mmctx
); 
 165         /* Initialize the secret state. */ 
 166         /* Init entropy pool */ 
 168         /* Init output generator */ 
 169         polllength 
= prng_slow_poll(bigbuf
,SPLEN
); 
 171         SHA1Update(ctx
,bigbuf
,polllength
); 
 173         prng_make_new_state(&p
->outstate
, buf
); 
 183 #endif  /* SLOW_POLL_ENABLE */ 
 185 /* In-place modifed bubble sort */ 
 187 bubbleSort(UINT 
*data
,UINT len
)  
 189         UINT i
,last
,newlast
,temp
; 
 197                         if(data
[i
+1] > data
[i
])  
 210 #pragma mark * * * Public functions * * *  
 212 /* Set up the PRNG */ 
 214 prngInitialize(PrngRef 
*prng
)  
 217         comp_error_status resp
; 
 218         prng_error_status retval 
= PRNG_ERR_LOW_MEMORY
; 
 225         /* Create the mutex */ 
 226         /* NOTE: on return the mutex should bve held, since our caller (prngInitialize) 
 229         if(mutexCreatorId
!=0) {return PRNG_ERR_REINIT
;} 
 230         Statmutex 
= CreateMutex(NULL
,TRUE
,NULL
); 
 231         if(Statmutex 
== NULL
) {mutexCreatorId 
= 0; return PRNG_ERR_MUTEX
;} 
 232         DuplicateHandle(GetCurrentProcess(),Statmutex
,GetCurrentProcess(),&mutex
,SYNCHRONIZE
,FALSE
,0); 
 233         mutexCreatorId 
= GetCurrentProcessId(); 
 234         #endif  /* MUTEX_ENABLE */ 
 237         mmp 
= mmMalloc(sizeof(PRNG
)); 
 244                 p 
= (PRNG
*)mmGetPtr(mmp
); 
 245                 memset(p
, 0, sizeof(PRNG
)); 
 248         /* Initialize Variables */ 
 249         for(i
=0;i
<TOTAL_SOURCES
;i
++)  
 252                 p
->poolEstBits
[i
] = 0; 
 256         /* Setup security on the registry so that remote users cannot predict the slow pool */ 
 257         prng_set_NT_security(); 
 260         /* Initialize the secret state. */ 
 261         /* FIXME - might want to make this an option here and have the caller 
 262          * do it after we return....? */ 
 265         prng_slow_init(p
);      /* Does a slow poll and then calls prng_make_state(...) */ 
 268         prng_do_SHA1(&p
->outstate
); 
 269         prng_make_new_state(&p
->outstate
, p
->outstate
.out
); 
 270 #endif  /* SLOW_POLL_ENABLE */ 
 272         /* Initialize compression routines */ 
 273         for(i
=0;i
<COMP_SOURCES
;i
++)  
 275                 resp 
= comp_init((p
->comp_state
)+i
); 
 276                 if(resp
!=COMP_SUCCESS
) {retval 
= PRNG_ERR_COMPRESSION
; goto cleanup_init
;} 
 279         p
->ready 
= PRNG_READY
; 
 285         /* Program failed on one of the mmmallocs */ 
 290         CloseHandle(Statmutex
); 
 295         return retval
; /* default PRNG_ERR_LOW_MEMORY */ 
 300 prngOutput(PRNG 
*p
, BYTE 
*outbuf
,UINT outbuflen
)  
 303         GEN_CTX 
*ctx 
= &p
->outstate
; 
 308         chASSERT(BACKTRACKLIMIT 
> 0); 
 310         for(i
=0;i
<outbuflen
;i
++,ctx
->index
++,ctx
->numout
++)  
 312                 /* Check backtracklimit */ 
 313                 if(ctx
->numout 
> BACKTRACKLIMIT
)  
 316                         prng_make_new_state(ctx
, ctx
->out
); 
 318                 /* Check position in IV */ 
 324                 outbuf
[i
] = (ctx
->out
)[ctx
->index
]; 
 331 /* Cause the PRNG to reseed now regardless of entropy pool */  
 332 /* Should this be public? */ 
 334 prngForceReseed(PRNG 
*p
, LONGLONG ticks
)  
 338         FILETIME a
,b
,c
,usertime
; 
 342 #if     defined(macintosh) || defined(__APPLE__) 
 343         #if             (defined(TARGET_API_MAC_OSX) || defined(KERNEL_BUILD)) 
 346         #else   TARGET_API_MAC_CARBON 
 347                 UnsignedWide    uwide
;          /* struct needed for Microseconds() */ 
 357         /* Set up start and end times */ 
 358         #if             defined(macintosh) || defined(__APPLE__) 
 359                 #if             (defined(TARGET_API_MAC_OSX) || defined(KERNEL_BUILD)) 
 360                         /* note we can't loop for more than a million microseconds */ 
 364                 gettimeofday(&tv
, NULL
); 
 366                         endTime 
= tv
.tv_usec 
+ ticks
; 
 367                         if(endTime 
> 1000000) { 
 368                                 /* handle rollover now */  
 371                 #else   /* TARGET_API_MAC_OSX */ 
 372                         Microseconds(&uwide
); 
 373                         start 
= UnsignedWideToUInt64(uwide
); 
 374                 #endif  /* TARGET_API_xxx */ 
 375         #endif  /* macintosh */ 
 378                 /* Do a couple of iterations between time checks */ 
 379                 prngOutput(p
, buf
,64); 
 380                 SHA1Update(&p
->pool
,buf
,64); 
 381                 prngOutput(p
, buf
,64); 
 382                 SHA1Update(&p
->pool
,buf
,64); 
 383                 prngOutput(p
, buf
,64); 
 384                 SHA1Update(&p
->pool
,buf
,64); 
 385                 prngOutput(p
, buf
,64); 
 386                 SHA1Update(&p
->pool
,buf
,64); 
 387                 prngOutput(p
, buf
,64); 
 388                 SHA1Update(&p
->pool
,buf
,64); 
 390 #if             defined(macintosh) || defined(__APPLE__) 
 391         #if             defined(TARGET_API_MAC_OSX) || defined(KERNEL_BUILD) 
 392         #ifdef TARGET_API_MAC_OSX 
 393             gettimeofday(&tv
, NULL
); 
 397         } while(tv
.tv_usec 
< endTime
); 
 399                 Microseconds(&uwide
); 
 400                 now 
= UnsignedWideToUInt64(uwide
); 
 401         } while ( (now
-start
) < ticks
) ; 
 404         } while ( (now
-start
) < ticks
) ; 
 406         SHA1Final(dig
,&p
->pool
); 
 407         SHA1Update(&p
->pool
,dig
,20);  
 408         SHA1Final(dig
,&p
->pool
); 
 410         /* Reset secret state */ 
 412         prng_make_new_state(&p
->outstate
,dig
); 
 414         /* Clear counter variables */ 
 415         for(i
=0;i
<TOTAL_SOURCES
;i
++)  
 418                 p
->poolEstBits
[i
] = 0; 
 422         trashMemory(dig
,20*sizeof(char)); 
 423         trashMemory(buf
,64*sizeof(char)); 
 429 /* Input a state into the PRNG */ 
 431 prngProcessSeedBuffer(PRNG 
*p
, BYTE 
*buf
,LONGLONG ticks
)  
 437         /* Put the data into the entropy, add some data from the unknown state, reseed */ 
 438         SHA1Update(&p
->pool
,buf
,20);                    /* Put it into the entropy pool */ 
 439         prng_do_SHA1(&p
->outstate
);                             /* Output 20 more bytes and     */ 
 440         SHA1Update(&p
->pool
,p
->outstate
.out
,20);/* add it to the pool as well.  */ 
 441         prngForceReseed(p
, ticks
);                              /* Do a reseed */ 
 442         return prngOutput(p
, buf
,20); /* Return the first 20 bytes of output in buf */ 
 446 /* Take some "random" data and make more "random-looking" data from it */ 
 447 /* note: this routine has no context, no mutex wrapper */ 
 449 prngStretch(BYTE 
*inbuf
,UINT inbuflen
,BYTE 
*outbuf
,UINT outbuflen
) { 
 457         if(inbuflen 
>= outbuflen
)  
 459                 memcpy(outbuf
,inbuf
,outbuflen
); 
 462         else  /* Extend using SHA1 hash of inbuf */ 
 465                 SHA1Update(&ctx
,inbuf
,inbuflen
); 
 467                 for(prev
=0,left
=outbuflen
;left
>0;prev
+=20,left
-=20)  
 469                         SHA1Update(&ctx
,dig
,20); 
 471                         memcpy(outbuf
+prev
,dig
,(left
>20)?20:left
); 
 473                 trashMemory(dig
,20*sizeof(BYTE
)); 
 478         return PRNG_ERR_PROGRAM_FLOW
; 
 482 /* Add entropy to the PRNG from a source */ 
 484 prngInput(PRNG 
*p
, BYTE 
*inbuf
,UINT inbuflen
,UINT poolnum
,UINT estbits
)  
 486         #ifndef YARROW_KERNEL 
 487         comp_error_status resp
; 
 493         if(poolnum 
>= TOTAL_SOURCES
) {return PRNG_ERR_OUT_OF_BOUNDS
;} 
 495         /* Add to entropy pool */ 
 496         SHA1Update(&p
->pool
,inbuf
,inbuflen
); 
 498         #ifndef YARROW_KERNEL 
 499         /* skip this step for the kernel */ 
 501         /* Update pool size, pool user estimate and pool compression context */ 
 502         p
->poolSize
[poolnum
] += inbuflen
; 
 503         p
->poolEstBits
[poolnum
] += estbits
; 
 504         if(poolnum
<COMP_SOURCES
) 
 506                 resp 
= comp_add_data((p
->comp_state
)+poolnum
,inbuf
,inbuflen
); 
 507                 if(resp
!=COMP_SUCCESS
) {return PRNG_ERR_COMPRESSION
;} 
 509         #endif  /* YARROW_KERNEL */ 
 516 /* If we have enough entropy, allow a reseed of the system */ 
 518 prngAllowReseed(PRNG 
*p
, LONGLONG ticks
)  
 520         UINT temp
[TOTAL_SOURCES
]; 
 526         comp_error_status resp
; 
 531         for(i
=0;i
<ENTROPY_SOURCES
;i
++) 
 533                 /* Make sure that compression-based entropy estimates are current */ 
 534 #ifndef KERNEL_BUILD // floating point in a kernel is BAD! 
 535                 resp 
= comp_get_ratio((p
->comp_state
)+i
,&ratio
); 
 536                 if(resp
!=COMP_SUCCESS
) {return PRNG_ERR_COMPRESSION
;} 
 537                 /* Use 4 instead of 8 to half compression estimate */ 
 538                 temp
[i
] = (int)(ratio
*p
->poolSize
[i
]*4); 
 540         temp
[i
] = p
->poolSize
[i
] * 4; 
 544         /* Use minumum of user and compression estimate for compressed sources */ 
 545         for(i
=ENTROPY_SOURCES
;i
<COMP_SOURCES
;i
++) 
 548                 /* Make sure that compression-based entropy estimates are current */ 
 549                 resp 
= comp_get_ratio((p
->comp_state
)+i
,&ratio
); 
 550                 if(resp
!=COMP_SUCCESS
) {return PRNG_ERR_COMPRESSION
;} 
 551                 /* Use 4 instead of 8 to half compression estimate */ 
 552                 temp
[i
] = _MIN((int)(ratio
*p
->poolSize
[i
]*4),(int)p
->poolEstBits
[i
]); 
 554         temp
[i
] = _MIN (p
->poolSize
[i
] * 4, p
->poolEstBits
[i
]); 
 558         /* Use user estimate for remaining sources */ 
 559         for(i
=COMP_SOURCES
;i
<TOTAL_SOURCES
;i
++) {temp
[i
] = p
->poolEstBits
[i
];} 
 562                 /* pointless if we're not ignoring any sources */ 
 563                 bubbleSort(temp
,TOTAL_SOURCES
); 
 565         for(i
=K
,sum
=0;i
<TOTAL_SOURCES
;sum
+=temp
[i
++]); /* Stupid C trick */ 
 567                 return prngForceReseed(p
, ticks
); 
 569                 return PRNG_ERR_NOT_ENOUGH_ENTROPY
; 
 571         return PRNG_ERR_PROGRAM_FLOW
; 
 575 /* Call a slow poll and insert the data into the entropy pool */ 
 576 static prng_error_status
 
 577 prngSlowPoll(PRNG 
*p
, UINT pollsize
) 
 581         prng_error_status retval
; 
 585         buf 
= (BYTE
*)malloc(pollsize
); 
 586         if(buf
==NULL
) {return PRNG_ERR_LOW_MEMORY
;} 
 587         len 
= prng_slow_poll(buf
,pollsize
);     /* OS specific call */ 
 588         retval 
= prngInput(p
, buf
,len
,SLOWPOLLSOURCE
, len 
* 8); 
 589         trashMemory(buf
,pollsize
); 
 594 #endif  /* SLOW_POLL_ENABLE */ 
 597 /* Delete the PRNG */ 
 604         if(GetCurrentProcessId()!=mutexCreatorId
) {return PRNG_ERR_WRONG_CALLER
;} 
 606         if(p
==NULL
) {return PRNG_SUCCESS
;} /* Well, there is nothing to destroy... */ 
 608         p
->ready 
= PRNG_NOT_READY
; 
 610         for(i
=0;i
<COMP_SOURCES
;i
++) 
 612                 comp_end((p
->comp_state
)+i
); 
 616         CloseHandle(Statmutex
);