]> git.saurik.com Git - apple/xnu.git/blob - osfmk/prng/YarrowCoreLib/src/prng.c
754935bc6f31c33be95f4d98dcf3a752790d9939
[apple/xnu.git] / osfmk / prng / YarrowCoreLib / src / prng.c
1 /*
2 * Copyright (c) 1999, 2000-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /*
30 File: prng.c
31
32 Contains: Core routines for the Counterpane Yarrow PRNG.
33
34 Written by: Counterpane, Inc.
35
36 Copyright: (c) 2000 by Apple Computer, Inc., all rights reserved.
37
38 Change History (most recent first):
39
40 02/10/99 dpm Created, based on Counterpane source.
41
42 */
43 /*
44 prng.c
45
46 Core routines for the Counterpane PRNG
47 */
48 #include "userdefines.h"
49 #include "assertverify.h"
50 #include "prng/YarrowCoreLib/include/yarrowUtils.h"
51
52 #if defined(macintosh) || defined(__APPLE__)
53 /* FIXME - this file needs to be in a platform-independent place */
54
55 #include "macOnly.h"
56 #endif /* macintosh */
57 #include "smf.h"
58 #include "sha1mod.h"
59 #include "entropysources.h"
60 #include "comp.h"
61 #include "prng/YarrowCoreLib/include/yarrow.h"
62 #include "prng.h"
63 #include "prngpriv.h"
64
65
66 #define _MAX(a,b) (((a)>(b))?(a):(b))
67 #define _MIN(a,b) (((a)<(b))?(a):(b))
68
69 #if defined(macintosh) || defined(__APPLE__)
70 /*
71 * No mutexes in this module for Macintosh/OSX. We handle the
72 * required locking elsewhere.
73 */
74 #define MUTEX_ENABLE 0
75
76 #include <string.h> /* memcpy, etc. */
77 #if TARGET_API_MAC_OSX
78 #include <sys/time.h> /* for timespec */
79 #elif TARGET_API_MAC_CARBON
80 #include <Timer.h> /* Microseconds */
81 #include <Math64.h>
82 #elif KERNEL_BUILD
83 #include <sys/time.h>
84 #elif MACH_KERNEL_PRIVATE
85 #include <mach/mach_time.h>
86 #include <mach/clock_types.h>
87 #else
88 #error Unknown TARGET_API
89 #endif /* TARGET_API */
90 #else
91 #define MUTEX_ENABLE 1
92 #endif /* macintosh */
93
94 #if MUTEX_ENABLE
95 static HANDLE Statmutex = NULL;
96 static DWORD mutexCreatorId = 0;
97 #endif
98
99 #if 0
100 #pragma mark -
101 #pragma mark * * * Static Utility functions * * *
102 #endif
103
104 /* All error checking should be done in the function that calls these */
105
106 /*
107 * out := SHA1(IV | out)
108 */
109 static void
110 prng_do_SHA1(GEN_CTX *ctx)
111 {
112 YSHA1_CTX sha;
113
114 YSHA1Init(&sha);
115 YSHA1Update(&sha,ctx->IV,20);
116 YSHA1Update(&sha,ctx->out,20);
117 YSHA1Final(ctx->out,&sha);
118 ctx->index = 0;
119 }
120
121 /*
122 * IV := newState
123 * out := SHA1(IV)
124 *
125 * Called from init, prngForceReseed(), and prngOutput()
126 * as anti-backtracking mechanism.
127 */
128 static void
129 prng_make_new_state(GEN_CTX *ctx,BYTE *newState)
130 {
131 YSHA1_CTX sha;
132
133 memcpy(ctx->IV,newState,20);
134 YSHA1Init(&sha);
135 YSHA1Update(&sha,ctx->IV,20);
136 YSHA1Final(ctx->out,&sha);
137 ctx->numout = 0;
138 ctx->index = 0;
139 }
140
141 #if SLOW_POLL_ENABLE
142
143
144 /* Initialize the secret state with a slow poll */
145 /* Currently only called from prngInitialize */
146
147 #define SPLEN 65536 /* 64K */
148
149 static void
150 prng_slow_init(PRNG *p)
151 /* This fails silently and must be fixed. */
152 {
153 YSHA1_CTX* ctx = NULL;
154 MMPTR mmctx = MM_NULL;
155 BYTE* bigbuf = NULL;
156 MMPTR mmbigbuf = MM_NULL;
157 BYTE* buf = NULL;
158 MMPTR mmbuf = MM_NULL;
159 DWORD polllength;
160
161 mmbigbuf = mmMalloc(SPLEN);
162 if(mmbigbuf == MM_NULL) {goto cleanup_slow_init;}
163 bigbuf = (BYTE*)mmGetPtr(mmbigbuf);
164
165 mmbuf = mmMalloc(20);
166 if(mmbuf == MM_NULL) {goto cleanup_slow_init;}
167 buf = (BYTE*)mmGetPtr(mmbuf);
168
169 mmctx = mmMalloc(sizeof(YSHA1_CTX));
170 if(mmctx == MM_NULL) {goto cleanup_slow_init;}
171 ctx = (YSHA1_CTX*)mmGetPtr(mmctx);
172
173
174 /* Initialize the secret state. */
175 /* Init entropy pool */
176 YSHA1Init(&p->pool);
177 /* Init output generator */
178 polllength = prng_slow_poll(bigbuf,SPLEN);
179 YSHA1Init(ctx);
180 YSHA1Update(ctx,bigbuf,polllength);
181 YSHA1Final(buf,ctx);
182 prng_make_new_state(&p->outstate, buf);
183
184 cleanup_slow_init:
185 mmFree(mmctx);
186 mmFree(mmbigbuf);
187 mmFree(mmbuf);
188
189 return;
190 }
191
192 #endif /* SLOW_POLL_ENABLE */
193
194 /* In-place modifed bubble sort */
195 static void
196 bubbleSort( UINT *data, LONG len )
197 {
198 LONG i,last,newlast;
199 UINT temp;
200
201 last = len-1;
202 while(last!=-1)
203 {
204 newlast = -1;
205 for(i=0;i<last;i++)
206 {
207 if(data[i+1] > data[i])
208 {
209 newlast = i;
210 temp = data[i];
211 data[i] = data[i+1];
212 data[i+1] = temp;
213 }
214 }
215 last = newlast;
216 }
217 }
218
219 #if 0
220 #pragma mark -
221 #pragma mark * * * Public functions * * *
222 #endif
223
224 /* Set up the PRNG */
225 prng_error_status
226 prngInitialize(PrngRef *prng)
227 {
228 UINT i;
229 comp_error_status resp;
230 prng_error_status retval = PRNG_ERR_LOW_MEMORY;
231 MMPTR mmp;
232 PRNG *p;
233
234 mmInit();
235
236 #if MUTEX_ENABLE
237 /* Create the mutex */
238 /* NOTE: on return the mutex should bve held, since our caller (prngInitialize)
239 * will release it.
240 */
241 if(mutexCreatorId!=0) {return PRNG_ERR_REINIT;}
242 Statmutex = CreateMutex(NULL,TRUE,NULL);
243 if(Statmutex == NULL) {mutexCreatorId = 0; return PRNG_ERR_MUTEX;}
244 DuplicateHandle(GetCurrentProcess(),Statmutex,GetCurrentProcess(),&mutex,SYNCHRONIZE,FALSE,0);
245 mutexCreatorId = GetCurrentProcessId();
246 #endif /* MUTEX_ENABLE */
247
248 /* Assign memory */
249 mmp = mmMalloc(sizeof(PRNG));
250 if(mmp==MM_NULL)
251 {
252 goto cleanup_init;
253 }
254 else
255 {
256 p = (PRNG*)mmGetPtr(mmp);
257 memset(p, 0, sizeof(PRNG));
258 }
259
260 /* Initialize Variables */
261 for(i=0;i<TOTAL_SOURCES;i++)
262 {
263 p->poolSize[i] = 0;
264 p->poolEstBits[i] = 0;
265 }
266
267 #ifdef WIN_NT
268 /* Setup security on the registry so that remote users cannot predict the slow pool */
269 prng_set_NT_security();
270 #endif
271
272 /* Initialize the secret state. */
273 /* FIXME - might want to make this an option here and have the caller
274 * do it after we return....? */
275 YSHA1Init(&p->pool);
276 #if SLOW_POLL_ENABLE
277 prng_slow_init(p); /* Does a slow poll and then calls prng_make_state(...) */
278 #else
279 /* NULL init */
280 prng_do_SHA1(&p->outstate);
281 prng_make_new_state(&p->outstate, p->outstate.out);
282 #endif /* SLOW_POLL_ENABLE */
283
284 /* Initialize compression routines */
285 for(i=0;i<COMP_SOURCES;i++)
286 {
287 resp = comp_init((p->comp_state)+i);
288 if(resp!=COMP_SUCCESS) {retval = PRNG_ERR_COMPRESSION; goto cleanup_init;}
289 }
290
291 p->ready = PRNG_READY;
292 *prng = (PrngRef)p;
293
294 return PRNG_SUCCESS;
295
296 cleanup_init:
297 /* Program failed on one of the mmmallocs */
298 mmFree(mmp);
299 mmp = MM_NULL;
300
301 #if MUTEX_ENABLE
302 CloseHandle(Statmutex);
303 Statmutex = NULL;
304 mutexCreatorId = 0;
305 #endif
306
307 return retval; /* default PRNG_ERR_LOW_MEMORY */
308 }
309
310 /* Provide output */
311 prng_error_status
312 prngOutput(PRNG *p, BYTE *outbuf,UINT outbuflen)
313 {
314 UINT i;
315 GEN_CTX *ctx = &p->outstate;
316
317 CHECKSTATE(p);
318 GENCHECK(p);
319 PCHECK(outbuf);
320 chASSERT(BACKTRACKLIMIT > 0);
321
322 for(i=0;i<outbuflen;i++,ctx->index++,ctx->numout++)
323 {
324 /* Check backtracklimit */
325 if(ctx->numout > BACKTRACKLIMIT)
326 {
327 prng_do_SHA1(ctx);
328 prng_make_new_state(ctx, ctx->out);
329 }
330 /* Check position in IV */
331 if(ctx->index>=20)
332 {
333 prng_do_SHA1(ctx);
334 }
335 /* Output data */
336 outbuf[i] = (ctx->out)[ctx->index];
337 }
338
339 return PRNG_SUCCESS;
340 }
341
342
343 /* Cause the PRNG to reseed now regardless of entropy pool */
344 /* Should this be public? */
345 prng_error_status
346 prngForceReseed(PRNG *p, LONGLONG ticks)
347 {
348 int i;
349 #ifdef WIN_NT
350 FILETIME a,b,c,usertime;
351 #endif
352 BYTE buf[64];
353 BYTE dig[20];
354 #if defined(macintosh) || defined(__APPLE__)
355 #if (defined(TARGET_API_MAC_OSX) || defined(KERNEL_BUILD))
356 struct timeval tv;
357 int64_t endTime, curTime;
358 #elif defined(MACH_KERNEL_PRIVATE)
359 int64_t endTime, curTime;
360 #else /* TARGET_API_MAC_CARBON */
361 UnsignedWide uwide; /* struct needed for Microseconds() */
362 LONGLONG start;
363 LONGLONG now;
364 #endif
365 #endif
366
367 CHECKSTATE(p);
368 POOLCHECK(p);
369 ZCHECK(ticks);
370
371 /* Set up start and end times */
372 #if defined(macintosh) || defined(__APPLE__)
373 #if (defined(TARGET_API_MAC_OSX) || defined(KERNEL_BUILD))
374 /* note we can't loop for more than a million microseconds */
375 #ifdef KERNEL_BUILD
376 microuptime (&tv);
377 #else
378 gettimeofday(&tv, NULL);
379 #endif
380 endTime = (int64_t)tv.tv_sec*1000000LL + (int64_t)tv.tv_usec + ticks;
381 #elif defined(MACH_KERNEL_PRIVATE)
382 endTime = mach_absolute_time() + (ticks*NSEC_PER_USEC);
383 #else /* TARGET_API_MAC_OSX */
384 Microseconds(&uwide);
385 start = UnsignedWideToUInt64(uwide);
386 #endif /* TARGET_API_xxx */
387 #endif /* macintosh */
388 do
389 {
390 /* Do a couple of iterations between time checks */
391 prngOutput(p, buf,64);
392 YSHA1Update(&p->pool,buf,64);
393 prngOutput(p, buf,64);
394 YSHA1Update(&p->pool,buf,64);
395 prngOutput(p, buf,64);
396 YSHA1Update(&p->pool,buf,64);
397 prngOutput(p, buf,64);
398 YSHA1Update(&p->pool,buf,64);
399 prngOutput(p, buf,64);
400 YSHA1Update(&p->pool,buf,64);
401
402 #if defined(macintosh) || defined(__APPLE__)
403 #if defined(TARGET_API_MAC_OSX) || defined(KERNEL_BUILD)
404 #ifdef TARGET_API_MAC_OSX
405 gettimeofday(&tv, NULL);
406 #else
407 microuptime (&tv);
408 curTime = (int64_t)tv.tv_sec*1000000LL + (int64_t)tv.tv_usec;
409 #endif
410 } while(curTime < endTime);
411 #elif defined(MACH_KERNEL_PRIVATE)
412 curTime = mach_absolute_time();
413 } while(curTime < endTime);
414 #else
415 Microseconds(&uwide);
416 now = UnsignedWideToUInt64(uwide);
417 } while ( (now-start) < ticks) ;
418 #endif
419 #else
420 } while ( (now-start) < ticks) ;
421 #endif
422 YSHA1Final(dig,&p->pool);
423 YSHA1Update(&p->pool,dig,20);
424 YSHA1Final(dig,&p->pool);
425
426 /* Reset secret state */
427 YSHA1Init(&p->pool);
428 prng_make_new_state(&p->outstate,dig);
429
430 /* Clear counter variables */
431 for(i=0;i<TOTAL_SOURCES;i++)
432 {
433 p->poolSize[i] = 0;
434 p->poolEstBits[i] = 0;
435 }
436
437 /* Cleanup memory */
438 trashMemory(dig,20*sizeof(char));
439 trashMemory(buf,64*sizeof(char));
440
441 return PRNG_SUCCESS;
442 }
443
444
445 /* Input a state into the PRNG */
446 prng_error_status
447 prngProcessSeedBuffer(PRNG *p, BYTE *buf,LONGLONG ticks)
448 {
449 CHECKSTATE(p);
450 GENCHECK(p);
451 PCHECK(buf);
452
453 /* Put the data into the entropy, add some data from the unknown state, reseed */
454 YSHA1Update(&p->pool,buf,20); /* Put it into the entropy pool */
455 prng_do_SHA1(&p->outstate); /* Output 20 more bytes and */
456 YSHA1Update(&p->pool,p->outstate.out,20);/* add it to the pool as well. */
457 prngForceReseed(p, ticks); /* Do a reseed */
458 return prngOutput(p, buf,20); /* Return the first 20 bytes of output in buf */
459 }
460
461
462 /* Take some "random" data and make more "random-looking" data from it */
463 /* note: this routine has no context, no mutex wrapper */
464 prng_error_status
465 prngStretch(BYTE *inbuf,UINT inbuflen,BYTE *outbuf,UINT outbuflen) {
466 long int left,prev;
467 YSHA1_CTX ctx;
468 BYTE dig[20];
469
470 PCHECK(inbuf);
471 PCHECK(outbuf);
472
473 if(inbuflen >= outbuflen)
474 {
475 memcpy(outbuf,inbuf,outbuflen);
476 return PRNG_SUCCESS;
477 }
478 else /* Extend using SHA1 hash of inbuf */
479 {
480 YSHA1Init(&ctx);
481 YSHA1Update(&ctx,inbuf,inbuflen);
482 YSHA1Final(dig,&ctx);
483 for(prev=0,left=outbuflen;left>0;prev+=20,left-=20)
484 {
485 YSHA1Update(&ctx,dig,20);
486 YSHA1Final(dig,&ctx);
487 memcpy(outbuf+prev,dig,(left>20)?20:left);
488 }
489 trashMemory(dig,20*sizeof(BYTE));
490
491 return PRNG_SUCCESS;
492 }
493
494 return PRNG_ERR_PROGRAM_FLOW;
495 }
496
497
498 /* Add entropy to the PRNG from a source */
499 prng_error_status
500 prngInput(PRNG *p, BYTE *inbuf,UINT inbuflen,UINT poolnum, __unused UINT estbits)
501 {
502 #ifndef YARROW_KERNEL
503 comp_error_status resp;
504 #endif
505
506 CHECKSTATE(p);
507 POOLCHECK(p);
508 PCHECK(inbuf);
509 if(poolnum >= TOTAL_SOURCES) {return PRNG_ERR_OUT_OF_BOUNDS;}
510
511 /* Add to entropy pool */
512 YSHA1Update(&p->pool,inbuf,inbuflen);
513
514 #ifndef YARROW_KERNEL
515 /* skip this step for the kernel */
516
517 /* Update pool size, pool user estimate and pool compression context */
518 p->poolSize[poolnum] += inbuflen;
519 p->poolEstBits[poolnum] += estbits;
520 if(poolnum<COMP_SOURCES)
521 {
522 resp = comp_add_data((p->comp_state)+poolnum,inbuf,inbuflen);
523 if(resp!=COMP_SUCCESS) {return PRNG_ERR_COMPRESSION;}
524 }
525 #endif /* YARROW_KERNEL */
526
527 return PRNG_SUCCESS;
528 }
529
530
531
532 /* If we have enough entropy, allow a reseed of the system */
533 prng_error_status
534 prngAllowReseed(PRNG *p, LONGLONG ticks)
535 {
536 UINT temp[TOTAL_SOURCES];
537 LONG i;
538 UINT sum;
539 #ifndef KERNEL_BUILD
540 float ratio;
541 #endif
542
543 #ifndef KERNEL_BUILD
544 comp_error_status resp;
545 #endif
546
547 CHECKSTATE(p);
548
549 for(i=0;i<ENTROPY_SOURCES;i++)
550 {
551 /* Make sure that compression-based entropy estimates are current */
552 #ifndef KERNEL_BUILD // floating point in a kernel is BAD!
553 resp = comp_get_ratio((p->comp_state)+i,&ratio);
554 if(resp!=COMP_SUCCESS) {return PRNG_ERR_COMPRESSION;}
555 /* Use 4 instead of 8 to half compression estimate */
556 temp[i] = (int)(ratio*p->poolSize[i]*4);
557 #else
558 temp[i] = p->poolSize[i] * 4;
559 #endif
560
561 }
562 /* Use minumum of user and compression estimate for compressed sources */
563 for(i=ENTROPY_SOURCES;i<COMP_SOURCES;i++)
564 {
565 #ifndef KERNEL_BUILD
566 /* Make sure that compression-based entropy estimates are current */
567 resp = comp_get_ratio((p->comp_state)+i,&ratio);
568 if(resp!=COMP_SUCCESS) {return PRNG_ERR_COMPRESSION;}
569 /* Use 4 instead of 8 to half compression estimate */
570 temp[i] = _MIN((int)(ratio*p->poolSize[i]*4),(int)p->poolEstBits[i]);
571 #else
572 temp[i] = _MIN (p->poolSize[i] * 4, p->poolEstBits[i]);
573 #endif
574
575 }
576 /* Use user estimate for remaining sources */
577 for(i=COMP_SOURCES;i<TOTAL_SOURCES;i++) {temp[i] = p->poolEstBits[i];}
578
579 if(K > 0) {
580 /* pointless if we're not ignoring any sources */
581 bubbleSort(temp,TOTAL_SOURCES);
582 }
583 for(i=K,sum=0;i<TOTAL_SOURCES;sum+=temp[i++]); /* Stupid C trick */
584 if(sum>THRESHOLD)
585 return prngForceReseed(p, ticks);
586 else
587 return PRNG_ERR_NOT_ENOUGH_ENTROPY;
588
589 return PRNG_ERR_PROGRAM_FLOW;
590 }
591
592 #if SLOW_POLL_ENABLE
593 /* Call a slow poll and insert the data into the entropy pool */
594 static prng_error_status
595 prngSlowPoll(PRNG *p, UINT pollsize)
596 {
597 BYTE *buf;
598 DWORD len;
599 prng_error_status retval;
600
601 CHECKSTATE(p);
602
603 buf = (BYTE*)malloc(pollsize);
604 if(buf==NULL) {return PRNG_ERR_LOW_MEMORY;}
605 len = prng_slow_poll(buf,pollsize); /* OS specific call */
606 retval = prngInput(p, buf,len,SLOWPOLLSOURCE, len * 8);
607 trashMemory(buf,pollsize);
608 free(buf);
609
610 return retval;
611 }
612 #endif /* SLOW_POLL_ENABLE */
613
614
615 /* Delete the PRNG */
616 prng_error_status
617 prngDestroy(PRNG *p)
618 {
619 UINT i;
620
621 #if MUTEX_ENABLE
622 if(GetCurrentProcessId()!=mutexCreatorId) {return PRNG_ERR_WRONG_CALLER;}
623 #endif
624 if(p==NULL) {return PRNG_SUCCESS;} /* Well, there is nothing to destroy... */
625
626 p->ready = PRNG_NOT_READY;
627
628 for(i=0;i<COMP_SOURCES;i++)
629 {
630 comp_end((p->comp_state)+i);
631 }
632
633 #if MUTEX_ENABLE
634 CloseHandle(Statmutex);
635 Statmutex = NULL;
636 mutexCreatorId = 0;
637 #endif
638
639 return PRNG_SUCCESS;
640 }
641
642