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
);