2 * Copyright (c) 1999, 2000-2013 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
32 Contains: Core routines for the Counterpane Yarrow PRNG.
34 Written by: Counterpane, Inc.
36 Copyright: (c) 2000 by Apple Computer, Inc., all rights reserved.
38 Change History (most recent first):
40 02/10/99 dpm Created, based on Counterpane source.
46 Core routines for the Counterpane PRNG
48 #include "userdefines.h"
49 #include "assertverify.h"
50 #include "prng/YarrowCoreLib/include/yarrowUtils.h"
52 #if defined(macintosh) || defined(__APPLE__)
53 /* FIXME - this file needs to be in a platform-independent place */
56 #endif /* macintosh */
59 #include "entropysources.h"
61 #include "prng/YarrowCoreLib/include/yarrow.h"
66 #define _MAX(a,b) (((a)>(b))?(a):(b))
67 #define _MIN(a,b) (((a)<(b))?(a):(b))
69 #if defined(macintosh) || defined(__APPLE__)
71 * No mutexes in this module for Macintosh/OSX. We handle the
72 * required locking elsewhere.
74 #define MUTEX_ENABLE 0
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 */
84 #elif MACH_KERNEL_PRIVATE
85 #include <mach/mach_time.h>
86 #include <mach/clock_types.h>
88 #error Unknown TARGET_API
89 #endif /* TARGET_API */
91 #define MUTEX_ENABLE 1
92 #endif /* macintosh */
95 static HANDLE Statmutex
= NULL
;
96 static DWORD mutexCreatorId
= 0;
101 #pragma mark * * * Static Utility functions * * *
104 /* All error checking should be done in the function that calls these */
107 * out := SHA1(IV | out)
110 prng_do_SHA1(GEN_CTX
*ctx
)
115 YSHA1Update(&sha
,ctx
->IV
,20);
116 YSHA1Update(&sha
,ctx
->out
,20);
117 YSHA1Final(ctx
->out
,&sha
);
125 * Called from init, prngForceReseed(), and prngOutput()
126 * as anti-backtracking mechanism.
129 prng_make_new_state(GEN_CTX
*ctx
,BYTE
*newState
)
133 memcpy(ctx
->IV
,newState
,20);
135 YSHA1Update(&sha
,ctx
->IV
,20);
136 YSHA1Final(ctx
->out
,&sha
);
144 /* Initialize the secret state with a slow poll */
145 /* Currently only called from prngInitialize */
147 #define SPLEN 65536 /* 64K */
150 prng_slow_init(PRNG
*p
)
151 /* This fails silently and must be fixed. */
153 YSHA1_CTX
* ctx
= NULL
;
154 MMPTR mmctx
= MM_NULL
;
156 MMPTR mmbigbuf
= MM_NULL
;
158 MMPTR mmbuf
= MM_NULL
;
161 mmbigbuf
= mmMalloc(SPLEN
);
162 if(mmbigbuf
== MM_NULL
) {goto cleanup_slow_init
;}
163 bigbuf
= (BYTE
*)mmGetPtr(mmbigbuf
);
165 mmbuf
= mmMalloc(20);
166 if(mmbuf
== MM_NULL
) {goto cleanup_slow_init
;}
167 buf
= (BYTE
*)mmGetPtr(mmbuf
);
169 mmctx
= mmMalloc(sizeof(YSHA1_CTX
));
170 if(mmctx
== MM_NULL
) {goto cleanup_slow_init
;}
171 ctx
= (YSHA1_CTX
*)mmGetPtr(mmctx
);
174 /* Initialize the secret state. */
175 /* Init entropy pool */
177 /* Init output generator */
178 polllength
= prng_slow_poll(bigbuf
,SPLEN
);
180 YSHA1Update(ctx
,bigbuf
,polllength
);
182 prng_make_new_state(&p
->outstate
, buf
);
192 #endif /* SLOW_POLL_ENABLE */
194 /* In-place modifed bubble sort */
196 bubbleSort( UINT
*data
, LONG len
)
207 if(data
[i
+1] > data
[i
])
221 #pragma mark * * * Public functions * * *
224 /* Set up the PRNG */
226 prngInitialize(PrngRef
*prng
)
229 comp_error_status resp
;
230 prng_error_status retval
= PRNG_ERR_LOW_MEMORY
;
237 /* Create the mutex */
238 /* NOTE: on return the mutex should bve held, since our caller (prngInitialize)
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 */
249 mmp
= mmMalloc(sizeof(PRNG
));
256 p
= (PRNG
*)mmGetPtr(mmp
);
257 memset(p
, 0, sizeof(PRNG
));
260 /* Initialize Variables */
261 for(i
=0;i
<TOTAL_SOURCES
;i
++)
264 p
->poolEstBits
[i
] = 0;
268 /* Setup security on the registry so that remote users cannot predict the slow pool */
269 prng_set_NT_security();
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....? */
277 prng_slow_init(p
); /* Does a slow poll and then calls prng_make_state(...) */
280 prng_do_SHA1(&p
->outstate
);
281 prng_make_new_state(&p
->outstate
, p
->outstate
.out
);
282 #endif /* SLOW_POLL_ENABLE */
284 /* Initialize compression routines */
285 for(i
=0;i
<COMP_SOURCES
;i
++)
287 resp
= comp_init((p
->comp_state
)+i
);
288 if(resp
!=COMP_SUCCESS
) {retval
= PRNG_ERR_COMPRESSION
; goto cleanup_init
;}
291 p
->ready
= PRNG_READY
;
297 /* Program failed on one of the mmmallocs */
302 CloseHandle(Statmutex
);
307 return retval
; /* default PRNG_ERR_LOW_MEMORY */
312 prngOutput(PRNG
*p
, BYTE
*outbuf
,UINT outbuflen
)
315 GEN_CTX
*ctx
= &p
->outstate
;
320 chASSERT(BACKTRACKLIMIT
> 0);
322 for(i
=0;i
<outbuflen
;i
++,ctx
->index
++,ctx
->numout
++)
324 /* Check backtracklimit */
325 if(ctx
->numout
> BACKTRACKLIMIT
)
328 prng_make_new_state(ctx
, ctx
->out
);
330 /* Check position in IV */
336 outbuf
[i
] = (ctx
->out
)[ctx
->index
];
343 /* Cause the PRNG to reseed now regardless of entropy pool */
344 /* Should this be public? */
346 prngForceReseed(PRNG
*p
, LONGLONG ticks
)
350 FILETIME a
,b
,c
,usertime
;
354 #if defined(macintosh) || defined(__APPLE__)
355 #if (defined(TARGET_API_MAC_OSX) || defined(KERNEL_BUILD))
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() */
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 */
378 gettimeofday(&tv
, NULL
);
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 */
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);
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
);
408 curTime
= (int64_t)tv
.tv_sec
*1000000LL + (int64_t)tv
.tv_usec
;
410 } while(curTime
< endTime
);
411 #elif defined(MACH_KERNEL_PRIVATE)
412 curTime
= mach_absolute_time();
413 } while(curTime
< endTime
);
415 Microseconds(&uwide
);
416 now
= UnsignedWideToUInt64(uwide
);
417 } while ( (now
-start
) < ticks
) ;
420 } while ( (now
-start
) < ticks
) ;
422 YSHA1Final(dig
,&p
->pool
);
423 YSHA1Update(&p
->pool
,dig
,20);
424 YSHA1Final(dig
,&p
->pool
);
426 /* Reset secret state */
428 prng_make_new_state(&p
->outstate
,dig
);
430 /* Clear counter variables */
431 for(i
=0;i
<TOTAL_SOURCES
;i
++)
434 p
->poolEstBits
[i
] = 0;
438 trashMemory(dig
,20*sizeof(char));
439 trashMemory(buf
,64*sizeof(char));
445 /* Input a state into the PRNG */
447 prngProcessSeedBuffer(PRNG
*p
, BYTE
*buf
,LONGLONG ticks
)
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 */
462 /* Take some "random" data and make more "random-looking" data from it */
463 /* note: this routine has no context, no mutex wrapper */
465 prngStretch(BYTE
*inbuf
,UINT inbuflen
,BYTE
*outbuf
,UINT outbuflen
) {
473 if(inbuflen
>= outbuflen
)
475 memcpy(outbuf
,inbuf
,outbuflen
);
478 else /* Extend using SHA1 hash of inbuf */
481 YSHA1Update(&ctx
,inbuf
,inbuflen
);
482 YSHA1Final(dig
,&ctx
);
483 for(prev
=0,left
=outbuflen
;left
>0;prev
+=20,left
-=20)
485 YSHA1Update(&ctx
,dig
,20);
486 YSHA1Final(dig
,&ctx
);
487 memcpy(outbuf
+prev
,dig
,(left
>20)?20:left
);
489 trashMemory(dig
,20*sizeof(BYTE
));
494 return PRNG_ERR_PROGRAM_FLOW
;
498 /* Add entropy to the PRNG from a source */
500 prngInput(PRNG
*p
, BYTE
*inbuf
,UINT inbuflen
,UINT poolnum
, __unused UINT estbits
)
502 #ifndef YARROW_KERNEL
503 comp_error_status resp
;
509 if(poolnum
>= TOTAL_SOURCES
) {return PRNG_ERR_OUT_OF_BOUNDS
;}
511 /* Add to entropy pool */
512 YSHA1Update(&p
->pool
,inbuf
,inbuflen
);
514 #ifndef YARROW_KERNEL
515 /* skip this step for the kernel */
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
)
522 resp
= comp_add_data((p
->comp_state
)+poolnum
,inbuf
,inbuflen
);
523 if(resp
!=COMP_SUCCESS
) {return PRNG_ERR_COMPRESSION
;}
525 #endif /* YARROW_KERNEL */
532 /* If we have enough entropy, allow a reseed of the system */
534 prngAllowReseed(PRNG
*p
, LONGLONG ticks
)
536 UINT temp
[TOTAL_SOURCES
];
544 comp_error_status resp
;
549 for(i
=0;i
<ENTROPY_SOURCES
;i
++)
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);
558 temp
[i
] = p
->poolSize
[i
] * 4;
562 /* Use minumum of user and compression estimate for compressed sources */
563 for(i
=ENTROPY_SOURCES
;i
<COMP_SOURCES
;i
++)
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
]);
572 temp
[i
] = _MIN (p
->poolSize
[i
] * 4, p
->poolEstBits
[i
]);
576 /* Use user estimate for remaining sources */
577 for(i
=COMP_SOURCES
;i
<TOTAL_SOURCES
;i
++) {temp
[i
] = p
->poolEstBits
[i
];}
580 /* pointless if we're not ignoring any sources */
581 bubbleSort(temp
,TOTAL_SOURCES
);
583 for(i
=K
,sum
=0;i
<TOTAL_SOURCES
;sum
+=temp
[i
++]); /* Stupid C trick */
585 return prngForceReseed(p
, ticks
);
587 return PRNG_ERR_NOT_ENOUGH_ENTROPY
;
589 return PRNG_ERR_PROGRAM_FLOW
;
593 /* Call a slow poll and insert the data into the entropy pool */
594 static prng_error_status
595 prngSlowPoll(PRNG
*p
, UINT pollsize
)
599 prng_error_status retval
;
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
);
612 #endif /* SLOW_POLL_ENABLE */
615 /* Delete the PRNG */
622 if(GetCurrentProcessId()!=mutexCreatorId
) {return PRNG_ERR_WRONG_CALLER
;}
624 if(p
==NULL
) {return PRNG_SUCCESS
;} /* Well, there is nothing to destroy... */
626 p
->ready
= PRNG_NOT_READY
;
628 for(i
=0;i
<COMP_SOURCES
;i
++)
630 comp_end((p
->comp_state
)+i
);
634 CloseHandle(Statmutex
);