]>
Commit | Line | Data |
---|---|---|
0b4e3aa0 | 1 | /* |
5d5c5d0d A |
2 | * Copyright (c)1999-2004 Apple Computer, Inc. All rights reserved. |
3 | * | |
8f6c56a5 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
0b4e3aa0 | 5 | * |
8f6c56a5 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. | |
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 | |
8ad349bb | 24 | * limitations under the License. |
8f6c56a5 A |
25 | * |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
0b4e3aa0 A |
27 | */ |
28 | ||
29 | #include <sys/param.h> | |
30 | #include <sys/systm.h> | |
31 | #include <sys/proc.h> | |
32 | #include <sys/errno.h> | |
33 | #include <sys/ioctl.h> | |
34 | #include <sys/conf.h> | |
35 | #include <sys/fcntl.h> | |
8ad349bb | 36 | #include <string.h> |
0b4e3aa0 A |
37 | #include <miscfs/devfs/devfs.h> |
38 | #include <kern/lock.h> | |
0b4e3aa0 A |
39 | #include <sys/time.h> |
40 | #include <sys/malloc.h> | |
91447636 | 41 | #include <sys/uio_internal.h> |
0b4e3aa0 A |
42 | |
43 | #include <dev/random/randomdev.h> | |
44 | #include <dev/random/YarrowCoreLib/include/yarrow.h> | |
8ad349bb | 45 | #include <crypto/sha1.h> |
0b4e3aa0 A |
46 | |
47 | #define RANDOM_MAJOR -1 /* let the kernel pick the device number */ | |
48 | ||
55e303ae A |
49 | d_ioctl_t random_ioctl; |
50 | ||
0b4e3aa0 A |
51 | /* |
52 | * A struct describing which functions will get invoked for certain | |
53 | * actions. | |
54 | */ | |
55 | static struct cdevsw random_cdevsw = | |
56 | { | |
57 | random_open, /* open */ | |
58 | random_close, /* close */ | |
59 | random_read, /* read */ | |
60 | random_write, /* write */ | |
91447636 A |
61 | random_ioctl, /* ioctl */ |
62 | (stop_fcn_t *)nulldev, /* stop */ | |
63 | (reset_fcn_t *)nulldev, /* reset */ | |
0b4e3aa0 A |
64 | NULL, /* tty's */ |
65 | eno_select, /* select */ | |
66 | eno_mmap, /* mmap */ | |
67 | eno_strat, /* strategy */ | |
68 | eno_getc, /* getc */ | |
69 | eno_putc, /* putc */ | |
70 | 0 /* type */ | |
71 | }; | |
72 | ||
73 | /* Used to detect whether we've already been initialized */ | |
74 | static int gRandomInstalled = 0; | |
75 | static PrngRef gPrngRef; | |
76 | static int gRandomError = 1; | |
77 | static mutex_t *gYarrowMutex = 0; | |
78 | ||
79 | #define RESEED_TICKS 50 /* how long a reseed operation can take */ | |
80 | ||
91447636 | 81 | |
8ad349bb A |
82 | enum {kBSizeInBits = 160}; // MUST be a multiple of 32!!! |
83 | enum {kBSizeInBytes = kBSizeInBits / 8}; | |
84 | typedef u_int32_t BlockWord; | |
85 | enum {kWordSizeInBits = 32}; | |
86 | enum {kBSize = 5}; | |
87 | typedef BlockWord Block[kBSize]; | |
88 | ||
89 | /* define prototypes to keep the compiler happy... */ | |
90 | ||
91 | void add_blocks(Block a, Block b, BlockWord carry); | |
92 | void fips_initialize(void); | |
93 | void random_block(Block b); | |
94 | ||
95 | /* | |
96 | * Get 120 bits from yarrow | |
97 | */ | |
98 | ||
99 | /* | |
100 | * add block b to block a | |
101 | */ | |
102 | void | |
103 | add_blocks(Block a, Block b, BlockWord carry) | |
104 | { | |
105 | int i = kBSize; | |
106 | while (--i >= 0) | |
107 | { | |
108 | u_int64_t c = (u_int64_t)carry + | |
109 | (u_int64_t)a[i] + | |
110 | (u_int64_t)b[i]; | |
111 | a[i] = c & ((1LL << kWordSizeInBits) - 1); | |
112 | carry = c >> kWordSizeInBits; | |
113 | } | |
114 | } | |
115 | ||
116 | ||
117 | ||
118 | struct sha1_ctxt g_sha1_ctx; | |
119 | char zeros[(512 - kBSizeInBits) / 8]; | |
120 | Block g_xkey; | |
121 | Block g_random_data; | |
122 | int g_bytes_used; | |
123 | ||
124 | /* | |
125 | * Setup for fips compliance | |
126 | */ | |
127 | ||
128 | /* | |
129 | * get a random block of data per fips 186-2 | |
130 | */ | |
131 | void | |
132 | random_block(Block b) | |
133 | { | |
134 | // do one iteration | |
135 | Block xSeed; | |
136 | prngOutput (gPrngRef, (BYTE*) &xSeed, sizeof (xSeed)); | |
137 | ||
138 | // add the seed to the previous value of g_xkey | |
139 | add_blocks (g_xkey, xSeed, 0); | |
140 | ||
141 | // compute "G" | |
142 | SHA1Update (&g_sha1_ctx, (const u_int8_t *) &g_xkey, sizeof (g_xkey)); | |
143 | ||
144 | // add zeros to fill the internal SHA-1 buffer | |
145 | SHA1Update (&g_sha1_ctx, (const u_int8_t *)zeros, sizeof (zeros)); | |
146 | ||
147 | // write the resulting block | |
148 | memmove(b, g_sha1_ctx.h.b8, sizeof (Block)); | |
149 | ||
150 | // fix up the next value of g_xkey | |
151 | add_blocks (g_xkey, b, 1); | |
152 | } | |
153 | ||
0b4e3aa0 A |
154 | /* |
155 | *Initialize ONLY the Yarrow generator. | |
156 | */ | |
8ad349bb A |
157 | void |
158 | PreliminarySetup(void) | |
0b4e3aa0 A |
159 | { |
160 | prng_error_status perr; | |
161 | struct timeval tt; | |
162 | char buffer [16]; | |
163 | ||
164 | /* create a Yarrow object */ | |
165 | perr = prngInitialize(&gPrngRef); | |
166 | if (perr != 0) { | |
167 | printf ("Couldn't initialize Yarrow, /dev/random will not work.\n"); | |
168 | return; | |
169 | } | |
170 | ||
171 | /* clear the error flag, reads and write should then work */ | |
172 | gRandomError = 0; | |
173 | ||
174 | /* get a little non-deterministic data as an initial seed. */ | |
175 | microtime(&tt); | |
176 | ||
177 | /* | |
178 | * So how much of the system clock is entropic? | |
179 | * It's hard to say, but assume that at least the | |
180 | * least significant byte of a 64 bit structure | |
181 | * is entropic. It's probably more, how can you figure | |
182 | * the exact time the user turned the computer on, for example. | |
183 | */ | |
184 | perr = prngInput(gPrngRef, (BYTE*) &tt, sizeof (tt), SYSTEM_SOURCE, 8); | |
185 | if (perr != 0) { | |
186 | /* an error, complain */ | |
187 | printf ("Couldn't seed Yarrow.\n"); | |
188 | return; | |
189 | } | |
190 | ||
191 | /* turn the data around */ | |
8ad349bb | 192 | perr = prngOutput(gPrngRef, (BYTE*)buffer, sizeof (buffer)); |
0b4e3aa0 A |
193 | |
194 | /* and scramble it some more */ | |
195 | perr = prngForceReseed(gPrngRef, RESEED_TICKS); | |
196 | ||
197 | /* make a mutex to control access */ | |
198 | gYarrowMutex = mutex_alloc(0); | |
8ad349bb A |
199 | |
200 | fips_initialize (); | |
201 | } | |
202 | ||
203 | void | |
204 | fips_initialize(void) | |
205 | { | |
206 | /* Read the initial value of g_xkey from yarrow */ | |
207 | prngOutput (gPrngRef, (BYTE*) &g_xkey, sizeof (g_xkey)); | |
208 | ||
209 | /* initialize our SHA1 generator */ | |
210 | SHA1Init (&g_sha1_ctx); | |
211 | ||
212 | /* other initializations */ | |
213 | memset (zeros, 0, sizeof (zeros)); | |
214 | g_bytes_used = 0; | |
215 | random_block(g_random_data); | |
0b4e3aa0 A |
216 | } |
217 | ||
218 | /* | |
219 | * Called to initialize our device, | |
220 | * and to register ourselves with devfs | |
221 | */ | |
222 | void | |
8ad349bb | 223 | random_init(void) |
0b4e3aa0 A |
224 | { |
225 | int ret; | |
226 | ||
227 | if (gRandomInstalled) | |
228 | return; | |
229 | ||
230 | /* install us in the file system */ | |
231 | gRandomInstalled = 1; | |
232 | ||
233 | /* setup yarrow and the mutex */ | |
234 | PreliminarySetup(); | |
235 | ||
236 | ret = cdevsw_add(RANDOM_MAJOR, &random_cdevsw); | |
237 | if (ret < 0) { | |
238 | printf("random_init: failed to allocate a major number!\n"); | |
239 | gRandomInstalled = 0; | |
240 | return; | |
241 | } | |
242 | ||
243 | devfs_make_node(makedev (ret, 0), DEVFS_CHAR, | |
55e303ae | 244 | UID_ROOT, GID_WHEEL, 0666, "random", 0); |
0b4e3aa0 A |
245 | |
246 | /* | |
247 | * also make urandom | |
248 | * (which is exactly the same thing in our context) | |
249 | */ | |
250 | devfs_make_node(makedev (ret, 1), DEVFS_CHAR, | |
55e303ae A |
251 | UID_ROOT, GID_WHEEL, 0666, "urandom", 0); |
252 | } | |
253 | ||
254 | int | |
91447636 A |
255 | random_ioctl( __unused dev_t dev, u_long cmd, __unused caddr_t data, |
256 | __unused int flag, __unused struct proc *p ) | |
55e303ae A |
257 | { |
258 | switch (cmd) { | |
259 | case FIONBIO: | |
260 | case FIOASYNC: | |
261 | break; | |
262 | default: | |
263 | return ENODEV; | |
264 | } | |
265 | ||
266 | return (0); | |
0b4e3aa0 A |
267 | } |
268 | ||
269 | /* | |
270 | * Open the device. Make sure init happened, and make sure the caller is | |
271 | * authorized. | |
272 | */ | |
273 | ||
274 | int | |
91447636 | 275 | random_open(__unused dev_t dev, int flags, __unused int devtype, __unused struct proc *p) |
0b4e3aa0 A |
276 | { |
277 | if (gRandomError != 0) { | |
278 | /* forget it, yarrow didn't come up */ | |
279 | return (ENOTSUP); | |
280 | } | |
281 | ||
282 | /* | |
283 | * if we are being opened for write, | |
284 | * make sure that we have privledges do so | |
285 | */ | |
286 | if (flags & FWRITE) { | |
287 | if (securelevel >= 2) | |
288 | return (EPERM); | |
55e303ae | 289 | #ifndef __APPLE__ |
91447636 | 290 | if ((securelevel >= 1) && proc_suser(p)) |
0b4e3aa0 | 291 | return (EPERM); |
55e303ae | 292 | #endif /* !__APPLE__ */ |
0b4e3aa0 A |
293 | } |
294 | ||
295 | return (0); | |
296 | } | |
297 | ||
298 | ||
299 | /* | |
300 | * close the device. | |
301 | */ | |
302 | ||
303 | int | |
91447636 | 304 | random_close(__unused dev_t dev, __unused int flags, __unused int mode, __unused struct proc *p) |
0b4e3aa0 A |
305 | { |
306 | return (0); | |
307 | } | |
308 | ||
309 | ||
310 | /* | |
311 | * Get entropic data from the Security Server, and use it to reseed the | |
312 | * prng. | |
313 | */ | |
314 | int | |
91447636 | 315 | random_write (__unused dev_t dev, struct uio *uio, __unused int ioflag) |
0b4e3aa0 A |
316 | { |
317 | int retCode = 0; | |
318 | char rdBuffer[256]; | |
319 | ||
320 | if (gRandomError != 0) { | |
321 | return (ENOTSUP); | |
322 | } | |
323 | ||
324 | /* get control of the Yarrow instance, Yarrow is NOT thread safe */ | |
325 | mutex_lock(gYarrowMutex); | |
326 | ||
327 | /* Security server is sending us entropy */ | |
328 | ||
91447636 | 329 | while (uio_resid(uio) > 0 && retCode == 0) { |
0b4e3aa0 | 330 | /* get the user's data */ |
91447636 A |
331 | // LP64todo - fix this! uio_resid may be 64-bit value |
332 | int bytesToInput = min(uio_resid(uio), sizeof (rdBuffer)); | |
0b4e3aa0 A |
333 | retCode = uiomove(rdBuffer, bytesToInput, uio); |
334 | if (retCode != 0) | |
335 | goto /*ugh*/ error_exit; | |
336 | ||
337 | /* put it in Yarrow */ | |
8ad349bb | 338 | if (prngInput(gPrngRef, (BYTE*)rdBuffer, |
c0fea474 A |
339 | bytesToInput, SYSTEM_SOURCE, |
340 | bytesToInput * 8) != 0) { | |
0b4e3aa0 A |
341 | retCode = EIO; |
342 | goto error_exit; | |
343 | } | |
344 | } | |
345 | ||
346 | /* force a reseed */ | |
347 | if (prngForceReseed(gPrngRef, RESEED_TICKS) != 0) { | |
348 | retCode = EIO; | |
349 | goto error_exit; | |
350 | } | |
351 | ||
352 | /* retCode should be 0 at this point */ | |
353 | ||
354 | error_exit: /* do this to make sure the mutex unlocks. */ | |
355 | mutex_unlock(gYarrowMutex); | |
356 | return (retCode); | |
357 | } | |
358 | ||
359 | /* | |
360 | * return data to the caller. Results unpredictable. | |
361 | */ | |
8ad349bb | 362 | int random_read(__unused dev_t dev, struct uio *uio, __unused int ioflag) |
0b4e3aa0 A |
363 | { |
364 | int retCode = 0; | |
8ad349bb | 365 | |
0b4e3aa0 A |
366 | if (gRandomError != 0) |
367 | return (ENOTSUP); | |
368 | ||
369 | /* lock down the mutex */ | |
370 | mutex_lock(gYarrowMutex); | |
371 | ||
8ad349bb A |
372 | int bytes_remaining = uio_resid(uio); |
373 | while (bytes_remaining > 0 && retCode == 0) { | |
0b4e3aa0 | 374 | /* get the user's data */ |
8ad349bb A |
375 | int bytes_to_read = 0; |
376 | ||
377 | int bytes_available = kBSizeInBytes - g_bytes_used; | |
378 | if (bytes_available == 0) | |
379 | { | |
380 | random_block(g_random_data); | |
381 | g_bytes_used = 0; | |
382 | bytes_available = kBSizeInBytes; | |
383 | } | |
384 | ||
385 | bytes_to_read = min (bytes_remaining, bytes_available); | |
386 | ||
387 | retCode = uiomove(((u_int8_t*)g_random_data)+ g_bytes_used, bytes_to_read, uio); | |
388 | g_bytes_used += bytes_to_read; | |
389 | ||
0b4e3aa0 A |
390 | if (retCode != 0) |
391 | goto error_exit; | |
8ad349bb A |
392 | |
393 | bytes_remaining = uio_resid(uio); | |
0b4e3aa0 A |
394 | } |
395 | ||
396 | retCode = 0; | |
397 | ||
398 | error_exit: | |
399 | mutex_unlock(gYarrowMutex); | |
400 | return retCode; | |
401 | } | |
402 | ||
403 | /* export good random numbers to the rest of the kernel */ | |
404 | void | |
405 | read_random(void* buffer, u_int numbytes) | |
406 | { | |
407 | if (gYarrowMutex == 0) { /* are we initialized? */ | |
408 | PreliminarySetup (); | |
409 | } | |
410 | ||
411 | mutex_lock(gYarrowMutex); | |
8ad349bb A |
412 | |
413 | int bytes_remaining = numbytes; | |
414 | while (bytes_remaining > 0) { | |
415 | int bytes_to_read = min(bytes_remaining, kBSizeInBytes - g_bytes_used); | |
416 | if (bytes_to_read == 0) | |
417 | { | |
418 | random_block(g_random_data); | |
419 | g_bytes_used = 0; | |
420 | bytes_to_read = min(bytes_remaining, kBSizeInBytes); | |
421 | } | |
422 | ||
423 | memmove (buffer, ((u_int8_t*)g_random_data)+ g_bytes_used, bytes_to_read); | |
424 | g_bytes_used += bytes_to_read; | |
425 | ||
426 | bytes_remaining -= bytes_to_read; | |
427 | } | |
428 | ||
0b4e3aa0 A |
429 | mutex_unlock(gYarrowMutex); |
430 | } | |
431 | ||
432 | /* | |
433 | * Return an unsigned long pseudo-random number. | |
434 | */ | |
435 | u_long | |
8ad349bb | 436 | RandomULong(void) |
0b4e3aa0 A |
437 | { |
438 | u_long buf; | |
439 | read_random(&buf, sizeof (buf)); | |
440 | return (buf); | |
441 | } | |
442 |