Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 | /* * Copyright (c) 2019 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #include <libkern/crypto/sha2.h> #include <libkern/crypto/crypto.h> #include <os/atomic_private.h> #include <kern/assert.h> #include <kern/percpu.h> #include <kern/zalloc.h> #include <kern/lock_group.h> #include <kern/locks.h> #include <kern/misc_protos.h> #include <pexpert/pexpert.h> #include <prng/entropy.h> #include <machine/machine_routines.h> #include <libkern/section_keywords.h> #include <sys/cdefs.h> // The number of samples we can hold in an entropy buffer. #define ENTROPY_MAX_SAMPLE_COUNT (2048) // The length of a bitmap_t array with one bit per sample of an // entropy buffer. #define ENTROPY_MAX_FILTER_COUNT (BITMAP_LEN(ENTROPY_MAX_SAMPLE_COUNT)) // The threshold of approximate linearity used in the entropy // filter. See the entropy_filter function for more discussion. #define ENTROPY_FILTER_THRESHOLD (8) // The state for a per-CPU entropy buffer. typedef struct entropy_cpu_data { // A buffer to hold entropy samples. entropy_sample_t samples[ENTROPY_MAX_SAMPLE_COUNT]; // A count of samples resident in the buffer. It also functions as // an index to the buffer. All entries at indices less than the // sample count are considered valid for consumption by the // reader. The reader resets this to zero after consuming the // available entropy. uint32_t _Atomic sample_count; } entropy_cpu_data_t; // This structure holds the state for an instance of a FIPS continuous // health test. In practice, we do not expect these tests to fail. typedef struct entropy_health_test { // The initial sample observed in this test instance. Tests look // for some repetition of the sample, either consecutively or // within a window. entropy_sample_t init_observation; // The count of times the initial observation has recurred within // the span of the current test. uint64_t observation_count; // The statistics are only relevant for telemetry and parameter // tuning. They do not drive any actual logic in the module. entropy_health_stats_t *stats; } entropy_health_test_t; typedef enum health_test_result { health_test_failure, health_test_success } health_test_result_t; // Along with various counters and the buffer itself, this includes // the state for two FIPS continuous health tests. typedef struct entropy_data { // State for a SHA512 computation. This is used to accumulate // entropy samples from across all CPUs. It is finalized when // entropy is provided to the consumer of this module. SHA512_CTX sha512_ctx; // A buffer to hold a bitmap with one bit per sample of an entropy // buffer. We are able to reuse this instance across all the // per-CPU entropy buffers to save space. bitmap_t filter[ENTROPY_MAX_FILTER_COUNT]; // A total count of entropy samples that have passed through this // structure. It is incremented as new samples are accumulated // from the various per-CPU structures. The "current" count of // samples is the difference between this field and the "read" // sample count below (which see). uint64_t total_sample_count; // Initially zero, this flag is reset to the current sample count // if and when we fail a health test. We consider the startup // health tests to be complete when the difference between the // total sample count and this field is at least 1024. In other // words, we must accumulate 1024 good samples to demonstrate // viability. We refuse to provide any entropy before that // threshold is reached. uint64_t startup_sample_count; // The count of samples from the last time we provided entropy to // the kernel RNG. We use this to compute how many new samples we // have to contribute. This value is also reset to the current // sample count in case of health test failure. uint64_t read_sample_count; // The lock group for this structure; see below. lck_grp_t lock_group; // This structure accumulates entropy samples from across all CPUs // for a single point of consumption protected by a mutex. lck_mtx_t mutex; // State for the Repetition Count Test. entropy_health_test_t repetition_count_test; // State for the Adaptive Proportion Test. entropy_health_test_t adaptive_proportion_test; } entropy_data_t; static entropy_cpu_data_t PERCPU_DATA(entropy_cpu_data); int entropy_health_startup_done; entropy_health_stats_t entropy_health_rct_stats; entropy_health_stats_t entropy_health_apt_stats; uint64_t entropy_filter_accepted_sample_count; uint64_t entropy_filter_rejected_sample_count; uint64_t entropy_filter_total_sample_count; static entropy_data_t entropy_data = { .repetition_count_test = { .init_observation = -1, .stats = &entropy_health_rct_stats, }, .adaptive_proportion_test = { .init_observation = -1, .stats = &entropy_health_apt_stats, }, }; #if ENTROPY_ANALYSIS_SUPPORTED __security_const_late int entropy_analysis_enabled; __security_const_late entropy_sample_t *entropy_analysis_buffer; __security_const_late uint32_t entropy_analysis_buffer_size; __security_const_late uint32_t entropy_analysis_filter_size; __security_const_late uint32_t entropy_analysis_max_sample_count; uint32_t entropy_analysis_sample_count; __startup_func static void entropy_analysis_init(uint32_t sample_count) { entropy_analysis_enabled = 1; entropy_analysis_max_sample_count = sample_count; entropy_analysis_buffer_size = sample_count * sizeof(entropy_sample_t); entropy_analysis_buffer = zalloc_permanent(entropy_analysis_buffer_size, ZALIGN(entropy_sample_t)); entropy_analysis_filter_size = (uint32_t) BITMAP_SIZE(entropy_analysis_max_sample_count); } static void entropy_analysis_store(entropy_sample_t sample) { uint32_t sample_count; uint32_t next_sample_count; os_atomic_rmw_loop(&entropy_analysis_sample_count, sample_count, next_sample_count, relaxed, { if (sample_count >= entropy_analysis_max_sample_count) { os_atomic_rmw_loop_give_up(return ); } next_sample_count = sample_count + 1; }); entropy_analysis_buffer[sample_count] = sample; } #endif // ENTROPY_ANALYSIS_SUPPORTED __startup_func void entropy_init(size_t seed_size, uint8_t *seed) { SHA512_Init(&entropy_data.sha512_ctx); SHA512_Update(&entropy_data.sha512_ctx, seed, seed_size); lck_grp_init(&entropy_data.lock_group, "entropy-data", LCK_GRP_ATTR_NULL); lck_mtx_init(&entropy_data.mutex, &entropy_data.lock_group, LCK_ATTR_NULL); #if ENTROPY_ANALYSIS_SUPPORTED // The below path is used only for testing. This boot arg is used // to collect raw entropy samples for offline analysis. uint32_t sample_count = 0; if (__improbable(PE_parse_boot_argn(ENTROPY_ANALYSIS_BOOTARG, &sample_count, sizeof(sample_count)))) { entropy_analysis_init(sample_count); } #endif // ENTROPY_ANALYSIS_SUPPORTED } void entropy_collect(void) { // This function is called from within the interrupt handler, so // we do not need to disable interrupts. entropy_cpu_data_t *e = PERCPU_GET(entropy_cpu_data); uint32_t sample_count = os_atomic_load(&e->sample_count, relaxed); assert3u(sample_count, <=, ENTROPY_MAX_SAMPLE_COUNT); // If the buffer is full, we return early without collecting // entropy. if (sample_count == ENTROPY_MAX_SAMPLE_COUNT) { return; } entropy_sample_t sample = (entropy_sample_t)ml_get_timebase_entropy(); e->samples[sample_count] = sample; // If the consumer has reset the sample count on us, the only // consequence is a dropped sample. We effectively abort the // entropy collection in this case. (void)os_atomic_cmpxchg(&e->sample_count, sample_count, sample_count + 1, release); #if ENTROPY_ANALYSIS_SUPPORTED // This code path is only used for testing. Its use is governed by // a boot arg; see its initialization above. if (__improbable(entropy_analysis_buffer)) { entropy_analysis_store(sample); } #endif // ENTROPY_ANALYSIS_SUPPORTED } // This filter looks at the 1st differential (differences of subsequent // timestamp values) and the 2nd differential (differences of subsequent // 1st differentials). This filter will detect sequences of timestamps // that are linear (that is, the 2nd differential is close to zero). // Timestamps with a 2nd differential above the threshold ENTROPY_FILTER_THRESHOLD // will be marked in the filter bitmap. 2nd differentials below the threshold // will not be counted nor included in the filter bitmap. // // For example imagine the following sequence of 8-bit timestamps: // // [25, 100, 175, 250, 69, 144, 219, 38, 113, 188] // // The 1st differential between timestamps is as follows: // // [75, 75, 75, 75, 75, 75, 75, 75, 75] // // The 2nd differential is as follows: // // [0, 0, 0, 0, 0, 0, 0, 0] // // The first two samples of any set of samples are always included as // there is no 2nd differential to compare against. Thus all but // the first two samples in this example will be removed. uint32_t entropy_filter(uint32_t sample_count, entropy_sample_t *samples, __assert_only uint32_t filter_count, bitmap_t *filter) { assert3u(filter_count, >=, BITMAP_LEN(sample_count)); bitmap_zero(filter, sample_count); // We always keep the first one (or two) sample(s) if we have at least one (or more) samples if (sample_count == 0) { return 0; } else if (sample_count == 1) { bitmap_set(filter, 0); return 1; } else if (sample_count == 2) { bitmap_set(filter, 0); bitmap_set(filter, 1); return 2; } else { bitmap_set(filter, 0); bitmap_set(filter, 1); } uint32_t filtered_sample_count = 2; // We don't care about underflows when computing any differential entropy_sample_t prev_1st_differential = samples[1] - samples[0]; for (uint i = 2; i < sample_count; i++) { entropy_sample_t curr_1st_differential = samples[i] - samples[i - 1]; entropy_sample_t curr_2nd_differential = curr_1st_differential - prev_1st_differential; if (curr_2nd_differential > ENTROPY_FILTER_THRESHOLD && curr_2nd_differential < ((entropy_sample_t) -ENTROPY_FILTER_THRESHOLD)) { bitmap_set(filter, i); filtered_sample_count += 1; } prev_1st_differential = curr_1st_differential; } return filtered_sample_count; } // For information on the following tests, see NIST SP 800-90B 4 // Health Tests. These tests are intended to detect catastrophic // degradations in entropy. As noted in that document: // // > Health tests are expected to raise an alarm in three cases: // > 1. When there is a significant decrease in the entropy of the // > outputs, // > 2. When noise source failures occur, or // > 3. When hardware fails, and implementations do not work // > correctly. // // Each entropy accumulator declines to release entropy until the // startup tests required by NIST are complete. In the event that a // health test does fail, all entropy accumulators are reset and // decline to release further entropy until their startup tests can be // repeated. static health_test_result_t add_observation(entropy_health_test_t *t, uint64_t bound) { t->observation_count += 1; t->stats->max_observation_count = MAX(t->stats->max_observation_count, (uint32_t)t->observation_count); if (__improbable(t->observation_count >= bound)) { t->stats->failure_count += 1; return health_test_failure; } return health_test_success; } static void reset_test(entropy_health_test_t *t, entropy_sample_t observation) { t->stats->reset_count += 1; t->init_observation = observation; t->observation_count = 1; t->stats->max_observation_count = MAX(t->stats->max_observation_count, (uint32_t)t->observation_count); } // 4.4.1 Repetition Count Test // // Like the name implies, this test counts consecutive occurrences of // the same value. // // We compute the bound C as: // // A = 2^-40 // H = 1 // C = 1 + ceil(-log(A, 2) / H) = 41 // // With A the acceptable chance of false positive and H a conservative // estimate for the min-entropy (in bits) of each sample. // // For more information, see tools/entropy_health_test_bounds.py. #define REPETITION_COUNT_BOUND (41) static health_test_result_t repetition_count_test(entropy_sample_t observation) { entropy_health_test_t *t = &entropy_data.repetition_count_test; if (t->init_observation == observation) { return add_observation(t, REPETITION_COUNT_BOUND); } else { reset_test(t, observation); } return health_test_success; } // 4.4.2 Adaptive Proportion Test // // This test counts occurrences of a value within a window of samples. // // We use a non-binary alphabet, giving us a window size of 512. (In // particular, we consider the least-significant byte of each time // sample.) // // Assuming one bit of entropy, we can compute the binomial cumulative // distribution function over 512 trials and choose a bound such that // the false positive rate is less than our target. // // For false positive rate and min-entropy estimate as above: // // A = 2^-40 // H = 1 // // We have our bound: // // C = 336 // // For more information, see tools/entropy_health_test_bounds.py. #define ADAPTIVE_PROPORTION_BOUND (336) #define ADAPTIVE_PROPORTION_WINDOW (512) // This mask definition requires the window be a power of two. static_assert(__builtin_popcount(ADAPTIVE_PROPORTION_WINDOW) == 1); #define ADAPTIVE_PROPORTION_INDEX_MASK (ADAPTIVE_PROPORTION_WINDOW - 1) static health_test_result_t adaptive_proportion_test(entropy_sample_t observation, uint32_t offset) { entropy_health_test_t *t = &entropy_data.adaptive_proportion_test; // We work in windows of size ADAPTIVE_PROPORTION_WINDOW, so we // can compute our index by taking the entropy buffer's overall // sample count plus the offset of this observation modulo the // window size. uint32_t index = (entropy_data.total_sample_count + offset) & ADAPTIVE_PROPORTION_INDEX_MASK; if (index == 0) { reset_test(t, observation); } else if (t->init_observation == observation) { return add_observation(t, ADAPTIVE_PROPORTION_BOUND); } return health_test_success; } static health_test_result_t entropy_health_test(uint32_t sample_count, entropy_sample_t *samples, __assert_only uint32_t filter_count, bitmap_t *filter) { health_test_result_t result = health_test_success; assert3u(filter_count, >=, BITMAP_LEN(sample_count)); for (uint32_t i = 0; i < sample_count; i += 1) { // We use the filter to determine if a given sample "counts" // or not. We skip the health tests on those samples that // failed the filter, since they are not expected to provide // any entropy. if (!bitmap_test(filter, i)) { continue; } // We only consider the low bits of each sample, since that is // where we expect the entropy to be concentrated. entropy_sample_t observation = samples[i] & 0xff; if (__improbable(repetition_count_test(observation) == health_test_failure)) { result = health_test_failure; } if (__improbable(adaptive_proportion_test(observation, i) == health_test_failure)) { result = health_test_failure; } } return result; } int32_t entropy_provide(size_t *entropy_size, void *entropy, __unused void *arg) { #if (DEVELOPMENT || DEBUG) if (*entropy_size < SHA512_DIGEST_LENGTH) { panic("[entropy_provide] recipient entropy buffer is too small"); } #endif int32_t sample_count = 0; *entropy_size = 0; // There is only one consumer (the kernel PRNG), but they could // try to consume entropy from different threads. We simply fail // if a consumption is already in progress. if (!lck_mtx_try_lock(&entropy_data.mutex)) { return sample_count; } health_test_result_t health_test_result = health_test_success; // We accumulate entropy from all CPUs. percpu_foreach(e, entropy_cpu_data) { // On each CPU, the sample count functions as an index into // the entropy buffer. All samples before that index are valid // for consumption. uint32_t cpu_sample_count = os_atomic_load(&e->sample_count, acquire); assert3u(cpu_sample_count, <=, ENTROPY_MAX_SAMPLE_COUNT); // We'll calculate how many samples that we would filter out // and only add that many to the total_sample_count. The bitmap // is not used during this operation. uint32_t filtered_sample_count = entropy_filter(cpu_sample_count, e->samples, ENTROPY_MAX_FILTER_COUNT, entropy_data.filter); assert3u(filtered_sample_count, <=, cpu_sample_count); entropy_filter_total_sample_count += cpu_sample_count; entropy_filter_accepted_sample_count += filtered_sample_count; entropy_filter_rejected_sample_count += (cpu_sample_count - filtered_sample_count); // The health test depends in part on the current state of // the entropy data, so we test the new sample before // accumulating it. health_test_result_t cpu_health_test_result = entropy_health_test(cpu_sample_count, e->samples, ENTROPY_MAX_FILTER_COUNT, entropy_data.filter); if (__improbable(cpu_health_test_result == health_test_failure)) { health_test_result = health_test_failure; } // We accumulate the samples regardless of whether the test // failed or a particular sample was filtered. It cannot hurt. entropy_data.total_sample_count += filtered_sample_count; SHA512_Update(&entropy_data.sha512_ctx, e->samples, cpu_sample_count * sizeof(e->samples[0])); // "Drain" the per-CPU buffer by resetting its sample count. os_atomic_store(&e->sample_count, 0, relaxed); } // We expect this never to happen. // // But if it does happen, we need to return negative to signal the // consumer (i.e. the kernel PRNG) that there has been a failure. if (__improbable(health_test_result == health_test_failure)) { entropy_health_startup_done = 0; entropy_data.startup_sample_count = entropy_data.total_sample_count; entropy_data.read_sample_count = entropy_data.total_sample_count; sample_count = -1; goto out; } // FIPS requires we pass our startup health tests before providing // any entropy. This condition is only true during startup and in // case of reset due to test failure. if (__improbable((entropy_data.total_sample_count - entropy_data.startup_sample_count) < 1024)) { goto out; } entropy_health_startup_done = 1; // The count of new samples from the consumer's perspective. int32_t n = (int32_t)(entropy_data.total_sample_count - entropy_data.read_sample_count); // Assuming one bit of entropy per sample, we buffer at least 512 // samples before delivering a high-entropy payload. In theory, // each payload will be a 512-bit seed with full entropy. // // We buffer an additional 64 bits of entropy to satisfy // over-sampling requirements in FIPS 140-3 IG. if (n < (512 + 64)) { goto out; } // Extract the entropy seed from the digest context and adjust // counters accordingly. SHA512_Final(entropy, &entropy_data.sha512_ctx); entropy_data.read_sample_count = entropy_data.total_sample_count; sample_count = n; *entropy_size = SHA512_DIGEST_LENGTH; // Reinitialize the digest context for future entropy // conditioning. SHA512_Init(&entropy_data.sha512_ctx); // To harden the entropy conditioner against an attacker with // partial or temporary control of interrupts, we roll the // extracted seed back into the new digest context. Assuming // we are able to reach a threshold of entropy, we can prevent // the attacker from predicting future output seeds. // // Along with the seed, we mix in a fixed label to personalize // this context. const char label[SHA512_BLOCK_LENGTH - SHA512_DIGEST_LENGTH] = "xnu entropy extract seed"; // We need the combined size of our inputs to equal the // internal SHA512 block size. This will force an additional // compression to provide backtracking resistance. assert3u(sizeof(label) + *entropy_size, ==, SHA512_BLOCK_LENGTH); SHA512_Update(&entropy_data.sha512_ctx, label, sizeof(label)); SHA512_Update(&entropy_data.sha512_ctx, entropy, *entropy_size); out: lck_mtx_unlock(&entropy_data.mutex); return sample_count; } |