]> git.saurik.com Git - apple/xnu.git/blame - osfmk/prng/YarrowCoreLib/src/prng.c
xnu-4570.71.2.tar.gz
[apple/xnu.git] / osfmk / prng / YarrowCoreLib / src / prng.c
CommitLineData
0b4e3aa0 1/*
39037602 2 * Copyright (c) 1999, 2000-2016 Apple Inc. All rights reserved.
5d5c5d0d 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0b4e3aa0 5 *
2d21ac55
A
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.
8f6c56a5 14 *
2d21ac55
A
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
0b4e3aa0
A
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"
fe8ab488 50#include "prng/YarrowCoreLib/include/yarrowUtils.h"
0b4e3aa0
A
51
52#if defined(macintosh) || defined(__APPLE__)
53/* FIXME - this file needs to be in a platform-independent place */
54
0b4e3aa0
A
55#include "macOnly.h"
56#endif /* macintosh */
57#include "smf.h"
58#include "sha1mod.h"
59#include "entropysources.h"
60#include "comp.h"
fe8ab488 61#include "prng/YarrowCoreLib/include/yarrow.h"
0b4e3aa0
A
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
fe8ab488
A
83 #include <sys/time.h>
84#elif MACH_KERNEL_PRIVATE
85 #include <mach/mach_time.h>
86 #include <mach/clock_types.h>
0b4e3aa0
A
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
95static HANDLE Statmutex = NULL;
96static DWORD mutexCreatorId = 0;
97#endif
98
b0d623f7 99#if 0
0b4e3aa0
A
100#pragma mark -
101#pragma mark * * * Static Utility functions * * *
b0d623f7 102#endif
0b4e3aa0
A
103
104/* All error checking should be done in the function that calls these */
105
106/*
107 * out := SHA1(IV | out)
108 */
109static void
110prng_do_SHA1(GEN_CTX *ctx)
111{
2d21ac55 112 YSHA1_CTX sha;
0b4e3aa0 113
2d21ac55
A
114 YSHA1Init(&sha);
115 YSHA1Update(&sha,ctx->IV,20);
116 YSHA1Update(&sha,ctx->out,20);
117 YSHA1Final(ctx->out,&sha);
0b4e3aa0
A
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 */
128static void
129prng_make_new_state(GEN_CTX *ctx,BYTE *newState)
130{
2d21ac55 131 YSHA1_CTX sha;
0b4e3aa0
A
132
133 memcpy(ctx->IV,newState,20);
2d21ac55
A
134 YSHA1Init(&sha);
135 YSHA1Update(&sha,ctx->IV,20);
136 YSHA1Final(ctx->out,&sha);
0b4e3aa0
A
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
149static void
150prng_slow_init(PRNG *p)
151/* This fails silently and must be fixed. */
152{
2d21ac55 153 YSHA1_CTX* ctx = NULL;
0b4e3aa0
A
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
2d21ac55 169 mmctx = mmMalloc(sizeof(YSHA1_CTX));
0b4e3aa0 170 if(mmctx == MM_NULL) {goto cleanup_slow_init;}
2d21ac55 171 ctx = (YSHA1_CTX*)mmGetPtr(mmctx);
0b4e3aa0
A
172
173
174 /* Initialize the secret state. */
175 /* Init entropy pool */
2d21ac55 176 YSHA1Init(&p->pool);
0b4e3aa0
A
177 /* Init output generator */
178 polllength = prng_slow_poll(bigbuf,SPLEN);
2d21ac55
A
179 YSHA1Init(ctx);
180 YSHA1Update(ctx,bigbuf,polllength);
181 YSHA1Final(buf,ctx);
0b4e3aa0
A
182 prng_make_new_state(&p->outstate, buf);
183
184cleanup_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 */
195static void
91447636 196bubbleSort( UINT *data, LONG len )
0b4e3aa0 197{
91447636
A
198 LONG i,last,newlast;
199 UINT temp;
0b4e3aa0
A
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
b0d623f7 219#if 0
0b4e3aa0
A
220#pragma mark -
221#pragma mark * * * Public functions * * *
b0d623f7 222#endif
0b4e3aa0
A
223
224/* Set up the PRNG */
225prng_error_status
226prngInitialize(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....? */
2d21ac55 275 YSHA1Init(&p->pool);
0b4e3aa0
A
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
296cleanup_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 */
311prng_error_status
312prngOutput(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? */
345prng_error_status
346prngForceReseed(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;
55e303ae 357 int64_t endTime, curTime;
fe8ab488
A
358 #elif defined(MACH_KERNEL_PRIVATE)
359 int64_t endTime, curTime;
55e303ae 360 #else /* TARGET_API_MAC_CARBON */
0b4e3aa0
A
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
55e303ae 376 microuptime (&tv);
0b4e3aa0
A
377 #else
378 gettimeofday(&tv, NULL);
379 #endif
55e303ae 380 endTime = (int64_t)tv.tv_sec*1000000LL + (int64_t)tv.tv_usec + ticks;
fe8ab488
A
381 #elif defined(MACH_KERNEL_PRIVATE)
382 endTime = mach_absolute_time() + (ticks*NSEC_PER_USEC);
0b4e3aa0
A
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);
2d21ac55 392 YSHA1Update(&p->pool,buf,64);
0b4e3aa0 393 prngOutput(p, buf,64);
2d21ac55 394 YSHA1Update(&p->pool,buf,64);
0b4e3aa0 395 prngOutput(p, buf,64);
2d21ac55 396 YSHA1Update(&p->pool,buf,64);
0b4e3aa0 397 prngOutput(p, buf,64);
2d21ac55 398 YSHA1Update(&p->pool,buf,64);
0b4e3aa0 399 prngOutput(p, buf,64);
2d21ac55 400 YSHA1Update(&p->pool,buf,64);
0b4e3aa0
A
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
55e303ae
A
407 microuptime (&tv);
408 curTime = (int64_t)tv.tv_sec*1000000LL + (int64_t)tv.tv_usec;
0b4e3aa0 409 #endif
55e303ae 410 } while(curTime < endTime);
fe8ab488
A
411 #elif defined(MACH_KERNEL_PRIVATE)
412 curTime = mach_absolute_time();
413 } while(curTime < endTime);
0b4e3aa0
A
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
2d21ac55
A
422 YSHA1Final(dig,&p->pool);
423 YSHA1Update(&p->pool,dig,20);
424 YSHA1Final(dig,&p->pool);
0b4e3aa0
A
425
426 /* Reset secret state */
2d21ac55 427 YSHA1Init(&p->pool);
0b4e3aa0
A
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 */
446prng_error_status
447prngProcessSeedBuffer(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 */
2d21ac55 454 YSHA1Update(&p->pool,buf,20); /* Put it into the entropy pool */
0b4e3aa0 455 prng_do_SHA1(&p->outstate); /* Output 20 more bytes and */
2d21ac55 456 YSHA1Update(&p->pool,p->outstate.out,20);/* add it to the pool as well. */
0b4e3aa0
A
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 */
464prng_error_status
465prngStretch(BYTE *inbuf,UINT inbuflen,BYTE *outbuf,UINT outbuflen) {
466 long int left,prev;
2d21ac55 467 YSHA1_CTX ctx;
0b4e3aa0
A
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 {
2d21ac55
A
480 YSHA1Init(&ctx);
481 YSHA1Update(&ctx,inbuf,inbuflen);
482 YSHA1Final(dig,&ctx);
0b4e3aa0
A
483 for(prev=0,left=outbuflen;left>0;prev+=20,left-=20)
484 {
2d21ac55
A
485 YSHA1Update(&ctx,dig,20);
486 YSHA1Final(dig,&ctx);
0b4e3aa0
A
487 memcpy(outbuf+prev,dig,(left>20)?20:left);
488 }
489 trashMemory(dig,20*sizeof(BYTE));
490
491 return PRNG_SUCCESS;
492 }
0b4e3aa0
A
493}
494
495
496/* Add entropy to the PRNG from a source */
497prng_error_status
91447636 498prngInput(PRNG *p, BYTE *inbuf,UINT inbuflen,UINT poolnum, __unused UINT estbits)
0b4e3aa0
A
499{
500 #ifndef YARROW_KERNEL
501 comp_error_status resp;
502 #endif
503
504 CHECKSTATE(p);
505 POOLCHECK(p);
506 PCHECK(inbuf);
507 if(poolnum >= TOTAL_SOURCES) {return PRNG_ERR_OUT_OF_BOUNDS;}
508
509 /* Add to entropy pool */
2d21ac55 510 YSHA1Update(&p->pool,inbuf,inbuflen);
0b4e3aa0
A
511
512 #ifndef YARROW_KERNEL
513 /* skip this step for the kernel */
514
515 /* Update pool size, pool user estimate and pool compression context */
516 p->poolSize[poolnum] += inbuflen;
517 p->poolEstBits[poolnum] += estbits;
518 if(poolnum<COMP_SOURCES)
519 {
520 resp = comp_add_data((p->comp_state)+poolnum,inbuf,inbuflen);
521 if(resp!=COMP_SUCCESS) {return PRNG_ERR_COMPRESSION;}
522 }
523 #endif /* YARROW_KERNEL */
524
525 return PRNG_SUCCESS;
526}
527
528
529
530/* If we have enough entropy, allow a reseed of the system */
531prng_error_status
532prngAllowReseed(PRNG *p, LONGLONG ticks)
533{
534 UINT temp[TOTAL_SOURCES];
91447636
A
535 LONG i;
536 UINT sum;
0b4e3aa0
A
537#ifndef KERNEL_BUILD
538 float ratio;
539#endif
540
91447636 541#ifndef KERNEL_BUILD
0b4e3aa0 542 comp_error_status resp;
91447636 543#endif
0b4e3aa0
A
544
545 CHECKSTATE(p);
546
547 for(i=0;i<ENTROPY_SOURCES;i++)
548 {
549 /* Make sure that compression-based entropy estimates are current */
550#ifndef KERNEL_BUILD // floating point in a kernel is BAD!
551 resp = comp_get_ratio((p->comp_state)+i,&ratio);
552 if(resp!=COMP_SUCCESS) {return PRNG_ERR_COMPRESSION;}
553 /* Use 4 instead of 8 to half compression estimate */
554 temp[i] = (int)(ratio*p->poolSize[i]*4);
555#else
556 temp[i] = p->poolSize[i] * 4;
557#endif
558
559 }
560 /* Use minumum of user and compression estimate for compressed sources */
561 for(i=ENTROPY_SOURCES;i<COMP_SOURCES;i++)
562 {
563#ifndef KERNEL_BUILD
564 /* Make sure that compression-based entropy estimates are current */
565 resp = comp_get_ratio((p->comp_state)+i,&ratio);
566 if(resp!=COMP_SUCCESS) {return PRNG_ERR_COMPRESSION;}
567 /* Use 4 instead of 8 to half compression estimate */
568 temp[i] = _MIN((int)(ratio*p->poolSize[i]*4),(int)p->poolEstBits[i]);
569#else
570 temp[i] = _MIN (p->poolSize[i] * 4, p->poolEstBits[i]);
571#endif
572
573 }
574 /* Use user estimate for remaining sources */
575 for(i=COMP_SOURCES;i<TOTAL_SOURCES;i++) {temp[i] = p->poolEstBits[i];}
576
577 if(K > 0) {
578 /* pointless if we're not ignoring any sources */
579 bubbleSort(temp,TOTAL_SOURCES);
580 }
581 for(i=K,sum=0;i<TOTAL_SOURCES;sum+=temp[i++]); /* Stupid C trick */
582 if(sum>THRESHOLD)
583 return prngForceReseed(p, ticks);
584 else
585 return PRNG_ERR_NOT_ENOUGH_ENTROPY;
0b4e3aa0
A
586}
587
588#if SLOW_POLL_ENABLE
589/* Call a slow poll and insert the data into the entropy pool */
590static prng_error_status
591prngSlowPoll(PRNG *p, UINT pollsize)
592{
593 BYTE *buf;
594 DWORD len;
595 prng_error_status retval;
596
597 CHECKSTATE(p);
598
599 buf = (BYTE*)malloc(pollsize);
600 if(buf==NULL) {return PRNG_ERR_LOW_MEMORY;}
601 len = prng_slow_poll(buf,pollsize); /* OS specific call */
602 retval = prngInput(p, buf,len,SLOWPOLLSOURCE, len * 8);
603 trashMemory(buf,pollsize);
604 free(buf);
605
606 return retval;
607}
608#endif /* SLOW_POLL_ENABLE */
609
610
611/* Delete the PRNG */
612prng_error_status
613prngDestroy(PRNG *p)
614{
615 UINT i;
616
617 #if MUTEX_ENABLE
618 if(GetCurrentProcessId()!=mutexCreatorId) {return PRNG_ERR_WRONG_CALLER;}
619 #endif
620 if(p==NULL) {return PRNG_SUCCESS;} /* Well, there is nothing to destroy... */
621
622 p->ready = PRNG_NOT_READY;
623
624 for(i=0;i<COMP_SOURCES;i++)
625 {
626 comp_end((p->comp_state)+i);
627 }
628
629 #if MUTEX_ENABLE
630 CloseHandle(Statmutex);
631 Statmutex = NULL;
632 mutexCreatorId = 0;
633 #endif
634
635 return PRNG_SUCCESS;
636}
637
638