2 * Copyright (c) 1999, 2000-2001 Apple Computer, 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 "dev/random/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 "dev/random/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 */
85 #error Unknown TARGET_API
86 #endif /* TARGET_API */
88 #define MUTEX_ENABLE 1
89 #endif /* macintosh */
92 static HANDLE Statmutex
= NULL
;
93 static DWORD mutexCreatorId
= 0;
98 #pragma mark * * * Static Utility functions * * *
101 /* All error checking should be done in the function that calls these */
104 * out := SHA1(IV | out)
107 prng_do_SHA1(GEN_CTX
*ctx
)
112 YSHA1Update(&sha
,ctx
->IV
,20);
113 YSHA1Update(&sha
,ctx
->out
,20);
114 YSHA1Final(ctx
->out
,&sha
);
122 * Called from init, prngForceReseed(), and prngOutput()
123 * as anti-backtracking mechanism.
126 prng_make_new_state(GEN_CTX
*ctx
,BYTE
*newState
)
130 memcpy(ctx
->IV
,newState
,20);
132 YSHA1Update(&sha
,ctx
->IV
,20);
133 YSHA1Final(ctx
->out
,&sha
);
141 /* Initialize the secret state with a slow poll */
142 /* Currently only called from prngInitialize */
144 #define SPLEN 65536 /* 64K */
147 prng_slow_init(PRNG
*p
)
148 /* This fails silently and must be fixed. */
150 YSHA1_CTX
* ctx
= NULL
;
151 MMPTR mmctx
= MM_NULL
;
153 MMPTR mmbigbuf
= MM_NULL
;
155 MMPTR mmbuf
= MM_NULL
;
158 mmbigbuf
= mmMalloc(SPLEN
);
159 if(mmbigbuf
== MM_NULL
) {goto cleanup_slow_init
;}
160 bigbuf
= (BYTE
*)mmGetPtr(mmbigbuf
);
162 mmbuf
= mmMalloc(20);
163 if(mmbuf
== MM_NULL
) {goto cleanup_slow_init
;}
164 buf
= (BYTE
*)mmGetPtr(mmbuf
);
166 mmctx
= mmMalloc(sizeof(YSHA1_CTX
));
167 if(mmctx
== MM_NULL
) {goto cleanup_slow_init
;}
168 ctx
= (YSHA1_CTX
*)mmGetPtr(mmctx
);
171 /* Initialize the secret state. */
172 /* Init entropy pool */
174 /* Init output generator */
175 polllength
= prng_slow_poll(bigbuf
,SPLEN
);
177 YSHA1Update(ctx
,bigbuf
,polllength
);
179 prng_make_new_state(&p
->outstate
, buf
);
189 #endif /* SLOW_POLL_ENABLE */
191 /* In-place modifed bubble sort */
193 bubbleSort( UINT
*data
, LONG len
)
204 if(data
[i
+1] > data
[i
])
218 #pragma mark * * * Public functions * * *
221 /* Set up the PRNG */
223 prngInitialize(PrngRef
*prng
)
226 comp_error_status resp
;
227 prng_error_status retval
= PRNG_ERR_LOW_MEMORY
;
234 /* Create the mutex */
235 /* NOTE: on return the mutex should bve held, since our caller (prngInitialize)
238 if(mutexCreatorId
!=0) {return PRNG_ERR_REINIT
;}
239 Statmutex
= CreateMutex(NULL
,TRUE
,NULL
);
240 if(Statmutex
== NULL
) {mutexCreatorId
= 0; return PRNG_ERR_MUTEX
;}
241 DuplicateHandle(GetCurrentProcess(),Statmutex
,GetCurrentProcess(),&mutex
,SYNCHRONIZE
,FALSE
,0);
242 mutexCreatorId
= GetCurrentProcessId();
243 #endif /* MUTEX_ENABLE */
246 mmp
= mmMalloc(sizeof(PRNG
));
253 p
= (PRNG
*)mmGetPtr(mmp
);
254 memset(p
, 0, sizeof(PRNG
));
257 /* Initialize Variables */
258 for(i
=0;i
<TOTAL_SOURCES
;i
++)
261 p
->poolEstBits
[i
] = 0;
265 /* Setup security on the registry so that remote users cannot predict the slow pool */
266 prng_set_NT_security();
269 /* Initialize the secret state. */
270 /* FIXME - might want to make this an option here and have the caller
271 * do it after we return....? */
274 prng_slow_init(p
); /* Does a slow poll and then calls prng_make_state(...) */
277 prng_do_SHA1(&p
->outstate
);
278 prng_make_new_state(&p
->outstate
, p
->outstate
.out
);
279 #endif /* SLOW_POLL_ENABLE */
281 /* Initialize compression routines */
282 for(i
=0;i
<COMP_SOURCES
;i
++)
284 resp
= comp_init((p
->comp_state
)+i
);
285 if(resp
!=COMP_SUCCESS
) {retval
= PRNG_ERR_COMPRESSION
; goto cleanup_init
;}
288 p
->ready
= PRNG_READY
;
294 /* Program failed on one of the mmmallocs */
299 CloseHandle(Statmutex
);
304 return retval
; /* default PRNG_ERR_LOW_MEMORY */
309 prngOutput(PRNG
*p
, BYTE
*outbuf
,UINT outbuflen
)
312 GEN_CTX
*ctx
= &p
->outstate
;
317 chASSERT(BACKTRACKLIMIT
> 0);
319 for(i
=0;i
<outbuflen
;i
++,ctx
->index
++,ctx
->numout
++)
321 /* Check backtracklimit */
322 if(ctx
->numout
> BACKTRACKLIMIT
)
325 prng_make_new_state(ctx
, ctx
->out
);
327 /* Check position in IV */
333 outbuf
[i
] = (ctx
->out
)[ctx
->index
];
340 /* Cause the PRNG to reseed now regardless of entropy pool */
341 /* Should this be public? */
343 prngForceReseed(PRNG
*p
, LONGLONG ticks
)
347 FILETIME a
,b
,c
,usertime
;
351 #if defined(macintosh) || defined(__APPLE__)
352 #if (defined(TARGET_API_MAC_OSX) || defined(KERNEL_BUILD))
354 int64_t endTime
, curTime
;
355 #else /* TARGET_API_MAC_CARBON */
356 UnsignedWide uwide
; /* struct needed for Microseconds() */
366 /* Set up start and end times */
367 #if defined(macintosh) || defined(__APPLE__)
368 #if (defined(TARGET_API_MAC_OSX) || defined(KERNEL_BUILD))
369 /* note we can't loop for more than a million microseconds */
373 gettimeofday(&tv
, NULL
);
375 endTime
= (int64_t)tv
.tv_sec
*1000000LL + (int64_t)tv
.tv_usec
+ ticks
;
376 #else /* TARGET_API_MAC_OSX */
377 Microseconds(&uwide
);
378 start
= UnsignedWideToUInt64(uwide
);
379 #endif /* TARGET_API_xxx */
380 #endif /* macintosh */
383 /* Do a couple of iterations between time checks */
384 prngOutput(p
, buf
,64);
385 YSHA1Update(&p
->pool
,buf
,64);
386 prngOutput(p
, buf
,64);
387 YSHA1Update(&p
->pool
,buf
,64);
388 prngOutput(p
, buf
,64);
389 YSHA1Update(&p
->pool
,buf
,64);
390 prngOutput(p
, buf
,64);
391 YSHA1Update(&p
->pool
,buf
,64);
392 prngOutput(p
, buf
,64);
393 YSHA1Update(&p
->pool
,buf
,64);
395 #if defined(macintosh) || defined(__APPLE__)
396 #if defined(TARGET_API_MAC_OSX) || defined(KERNEL_BUILD)
397 #ifdef TARGET_API_MAC_OSX
398 gettimeofday(&tv
, NULL
);
401 curTime
= (int64_t)tv
.tv_sec
*1000000LL + (int64_t)tv
.tv_usec
;
403 } while(curTime
< endTime
);
405 Microseconds(&uwide
);
406 now
= UnsignedWideToUInt64(uwide
);
407 } while ( (now
-start
) < ticks
) ;
410 } while ( (now
-start
) < ticks
) ;
412 YSHA1Final(dig
,&p
->pool
);
413 YSHA1Update(&p
->pool
,dig
,20);
414 YSHA1Final(dig
,&p
->pool
);
416 /* Reset secret state */
418 prng_make_new_state(&p
->outstate
,dig
);
420 /* Clear counter variables */
421 for(i
=0;i
<TOTAL_SOURCES
;i
++)
424 p
->poolEstBits
[i
] = 0;
428 trashMemory(dig
,20*sizeof(char));
429 trashMemory(buf
,64*sizeof(char));
435 /* Input a state into the PRNG */
437 prngProcessSeedBuffer(PRNG
*p
, BYTE
*buf
,LONGLONG ticks
)
443 /* Put the data into the entropy, add some data from the unknown state, reseed */
444 YSHA1Update(&p
->pool
,buf
,20); /* Put it into the entropy pool */
445 prng_do_SHA1(&p
->outstate
); /* Output 20 more bytes and */
446 YSHA1Update(&p
->pool
,p
->outstate
.out
,20);/* add it to the pool as well. */
447 prngForceReseed(p
, ticks
); /* Do a reseed */
448 return prngOutput(p
, buf
,20); /* Return the first 20 bytes of output in buf */
452 /* Take some "random" data and make more "random-looking" data from it */
453 /* note: this routine has no context, no mutex wrapper */
455 prngStretch(BYTE
*inbuf
,UINT inbuflen
,BYTE
*outbuf
,UINT outbuflen
) {
463 if(inbuflen
>= outbuflen
)
465 memcpy(outbuf
,inbuf
,outbuflen
);
468 else /* Extend using SHA1 hash of inbuf */
471 YSHA1Update(&ctx
,inbuf
,inbuflen
);
472 YSHA1Final(dig
,&ctx
);
473 for(prev
=0,left
=outbuflen
;left
>0;prev
+=20,left
-=20)
475 YSHA1Update(&ctx
,dig
,20);
476 YSHA1Final(dig
,&ctx
);
477 memcpy(outbuf
+prev
,dig
,(left
>20)?20:left
);
479 trashMemory(dig
,20*sizeof(BYTE
));
484 return PRNG_ERR_PROGRAM_FLOW
;
488 /* Add entropy to the PRNG from a source */
490 prngInput(PRNG
*p
, BYTE
*inbuf
,UINT inbuflen
,UINT poolnum
, __unused UINT estbits
)
492 #ifndef YARROW_KERNEL
493 comp_error_status resp
;
499 if(poolnum
>= TOTAL_SOURCES
) {return PRNG_ERR_OUT_OF_BOUNDS
;}
501 /* Add to entropy pool */
502 YSHA1Update(&p
->pool
,inbuf
,inbuflen
);
504 #ifndef YARROW_KERNEL
505 /* skip this step for the kernel */
507 /* Update pool size, pool user estimate and pool compression context */
508 p
->poolSize
[poolnum
] += inbuflen
;
509 p
->poolEstBits
[poolnum
] += estbits
;
510 if(poolnum
<COMP_SOURCES
)
512 resp
= comp_add_data((p
->comp_state
)+poolnum
,inbuf
,inbuflen
);
513 if(resp
!=COMP_SUCCESS
) {return PRNG_ERR_COMPRESSION
;}
515 #endif /* YARROW_KERNEL */
522 /* If we have enough entropy, allow a reseed of the system */
524 prngAllowReseed(PRNG
*p
, LONGLONG ticks
)
526 UINT temp
[TOTAL_SOURCES
];
534 comp_error_status resp
;
539 for(i
=0;i
<ENTROPY_SOURCES
;i
++)
541 /* Make sure that compression-based entropy estimates are current */
542 #ifndef KERNEL_BUILD // floating point in a kernel is BAD!
543 resp
= comp_get_ratio((p
->comp_state
)+i
,&ratio
);
544 if(resp
!=COMP_SUCCESS
) {return PRNG_ERR_COMPRESSION
;}
545 /* Use 4 instead of 8 to half compression estimate */
546 temp
[i
] = (int)(ratio
*p
->poolSize
[i
]*4);
548 temp
[i
] = p
->poolSize
[i
] * 4;
552 /* Use minumum of user and compression estimate for compressed sources */
553 for(i
=ENTROPY_SOURCES
;i
<COMP_SOURCES
;i
++)
556 /* Make sure that compression-based entropy estimates are current */
557 resp
= comp_get_ratio((p
->comp_state
)+i
,&ratio
);
558 if(resp
!=COMP_SUCCESS
) {return PRNG_ERR_COMPRESSION
;}
559 /* Use 4 instead of 8 to half compression estimate */
560 temp
[i
] = _MIN((int)(ratio
*p
->poolSize
[i
]*4),(int)p
->poolEstBits
[i
]);
562 temp
[i
] = _MIN (p
->poolSize
[i
] * 4, p
->poolEstBits
[i
]);
566 /* Use user estimate for remaining sources */
567 for(i
=COMP_SOURCES
;i
<TOTAL_SOURCES
;i
++) {temp
[i
] = p
->poolEstBits
[i
];}
570 /* pointless if we're not ignoring any sources */
571 bubbleSort(temp
,TOTAL_SOURCES
);
573 for(i
=K
,sum
=0;i
<TOTAL_SOURCES
;sum
+=temp
[i
++]); /* Stupid C trick */
575 return prngForceReseed(p
, ticks
);
577 return PRNG_ERR_NOT_ENOUGH_ENTROPY
;
579 return PRNG_ERR_PROGRAM_FLOW
;
583 /* Call a slow poll and insert the data into the entropy pool */
584 static prng_error_status
585 prngSlowPoll(PRNG
*p
, UINT pollsize
)
589 prng_error_status retval
;
593 buf
= (BYTE
*)malloc(pollsize
);
594 if(buf
==NULL
) {return PRNG_ERR_LOW_MEMORY
;}
595 len
= prng_slow_poll(buf
,pollsize
); /* OS specific call */
596 retval
= prngInput(p
, buf
,len
,SLOWPOLLSOURCE
, len
* 8);
597 trashMemory(buf
,pollsize
);
602 #endif /* SLOW_POLL_ENABLE */
605 /* Delete the PRNG */
612 if(GetCurrentProcessId()!=mutexCreatorId
) {return PRNG_ERR_WRONG_CALLER
;}
614 if(p
==NULL
) {return PRNG_SUCCESS
;} /* Well, there is nothing to destroy... */
616 p
->ready
= PRNG_NOT_READY
;
618 for(i
=0;i
<COMP_SOURCES
;i
++)
620 comp_end((p
->comp_state
)+i
);
624 CloseHandle(Statmutex
);