2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
20 File: MacYarrow_OSX.cpp
22 Contains: Yarrow RNG, OS X version.
24 Written by: Doug Mitchell
26 Copyright: (c) 2000 by Apple Computer, Inc., all rights reserved.
28 Change History (most recent first):
34 #include "MacYarrow_OSX.h"
35 #include "entropyFile.h"
36 #include "systemEntropy.h"
38 #include <Security/debugging.h>
39 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
42 #include <sys/types.h>
46 /* moved to Carbon.framework, FIXME */
47 // #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/Power.h> /* HardDiskPowered() */
49 static int HardDiskPowered() { return 1; }
58 * We collect system entropy every SYSTEM_ENTROPY_COLLECT_INTERVAL milliseconds.
60 #define SYSTEM_ENTROPY_COLLECT_INTERVAL (10 * 1000)
63 * Update system entropy file every UPDATE_SYSTEM_ENTROPY_FILE seconds.
65 #define UPDATE_SYSTEM_ENTROPY_FILE (30)
67 #else /* QUICK_TEST */
71 #define SYSTEM_ENTROPY_COLLECT_INTERVAL (10 * 60 * 1000)
72 #define UPDATE_SYSTEM_ENTROPY_FILE (60 * 60)
74 #endif /* QUICK_TEST */
77 * State of pending timer.
80 kYTSUninitialized
= 0,
81 kYTSCollecting
, // while gathering entropy
82 kYTSCollectingInit
, // while gathering entropy the first time
87 * When collecting system entropy, try for this many bytes.
89 #define SYSTEM_ENTROPY_SIZE 20
92 * Maintain an entropy file of this size.
94 #define ENTROPY_FILE_SIZE 20
97 * Microseconds to crunch in prngAllowReseed()
99 #define RESEED_TICKS 1000
102 * The single process-wide yarrow PRNG object and associated timer state.
103 * All of the code in this module runs in a single thread, owned by
104 * the YarrowServer object, so no locking is needed.
107 static yarrowTimerState timerState
= kYTSUninitialized
;
108 static struct timeval lastFileUpdate
;
110 static int gDevRandomRef
= -1;
113 * Reusable init. Currently called from the YarrowServer constructor.
115 OSStatus
yarrowServerInit(
116 const char *entropyFilePath
,
117 unsigned *firstTimeout
) // RETURNED, first timeout in milliseconds
119 UInt8 entropyFileData
[ENTROPY_FILE_SIZE
];
124 gDevRandomRef
= open ("/dev/random", O_RDWR
);
125 if (gDevRandomRef
== -1) {
130 * read entropy file, add contents to system entropy pool.
131 * It's not an error if there is no entropy file; this
132 * should only happen the first time this server runs on a given
135 gettimeofday(&lastFileUpdate
, NULL
);
136 setEntropyFilePath(entropyFilePath
);
137 ortn
= readEntropyFile(entropyFileData
,
140 if((ortn
== noErr
) && (actLen
> 0))
141 write(gDevRandomRef
, entropyFileData
, actLen
);
142 memset(entropyFileData
, 0, actLen
);
145 * Start collecting system entropy; schedule a timer event to gather
146 * it and add it to the pool.
148 systemEntropyBegin(SYSTEM_ENTROPY_SIZE
);
149 *firstTimeout
= SYSTEM_ENTROPY_COLLECT_TIME
;
150 timerState
= kYTSCollectingInit
;
156 void yarrowServerFini()
161 * Add some entropy to the pool. The only "known" failure here is a
162 * result of a failure of this library'e early init.
164 OSStatus
yarrowAddEntropy(
167 UInt32 bitsOfEntropy
,
168 unsigned *nextTimeout
) // RETURNED, next timeout in ms, 0 means none (leave
171 OSStatus rCode
= noErr
;
173 if (gDevRandomRef
== -1) { // did the system not open properly?
177 int result
= write (gDevRandomRef
, bytes
, numBytes
);
182 debug("yarrow", "adding %ld bytes of entropy", numBytes
);
185 * Asynchronously - because this can be time-consuming -
186 * add some system entropy too. This prevents clients from
187 * overwhelming the entropy pool with its own (untrusted) data.
188 * Skip this step if we happen to be collecting entropy at the
191 if(timerState
== kYTSSleeping
) {
192 systemEntropyBegin(SYSTEM_ENTROPY_SIZE
);
193 timerState
= kYTSCollecting
;
194 *nextTimeout
= SYSTEM_ENTROPY_COLLECT_TIME
;
202 * Get some random data. Caller mallocs the memory.
204 OSStatus
yarrowGetRandomBytes(
208 if (gDevRandomRef
== -1) {
212 int result
= read (gDevRandomRef
, bytes
, numBytes
);
222 * Handle timer event. Returns next timeout in milliseconds.
224 unsigned yarrowTimerEvent()
226 UInt8 sysEntropyData
[SYSTEM_ENTROPY_SIZE
];
228 UInt32 numSysEntropyBits
;
230 unsigned nextTimeout
;
234 case kYTSCollectingInit
:
236 * Entropy collection in progress; finish the operation,
237 * gather result, add to entropy pool.
239 debug("yarrowtimer", "collecting system entropy");
240 nextTimeout
= SYSTEM_ENTROPY_COLLECT_INTERVAL
;
241 if(rtn
= systemEntropyCollect(sysEntropyData
, SYSTEM_ENTROPY_SIZE
,
242 &numSysBytes
, &numSysEntropyBits
)) {
243 errorLog1("systemEntropyCollect() returned %d; aborting\n",
245 timerState
= kYTSSleeping
;
250 yarrowAddEntropy (sysEntropyData
, numSysBytes
, 0, &dummy
);
252 timerState
= kYTSSleeping
;
255 * Is it time to update the system entropy file?
259 gettimeofday(&now
, NULL
);
260 if( ( (now
.tv_sec
- lastFileUpdate
.tv_sec
) > UPDATE_SYSTEM_ENTROPY_FILE
) &&
261 HardDiskPowered() ) {
263 UInt8 entropyFileData
[ENTROPY_FILE_SIZE
];
266 debug("yarrow", "writing new entropy file");
268 yarrowGetRandomBytes (entropyFileData
, ENTROPY_FILE_SIZE
);
270 ortn
= writeEntropyFile(entropyFileData
, ENTROPY_FILE_SIZE
);
272 errorLog1("....writeEntropyFile returned %d\n", ortn
);
274 lastFileUpdate
= now
;
279 /* start to gather entropy */
280 debug("yarrowtimer", "start gathering entropy");
281 systemEntropyBegin(SYSTEM_ENTROPY_SIZE
);
282 timerState
= kYTSCollecting
;
283 nextTimeout
= SYSTEM_ENTROPY_COLLECT_TIME
;
287 errorLog1("yarrowTimerEvent with timerState %d\n", timerState
);
288 nextTimeout
= SYSTEM_ENTROPY_COLLECT_INTERVAL
;
291 debug("yarrowtimer", "timer rescheduling for %d msecs", nextTimeout
);