17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 502f574f0Skais * Common Development and Distribution License (the "License"). 602f574f0Skais * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 223364c169SVladimir Kotal * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* 277c478bd9Sstevel@tonic-gate * This file implements the interfaces that the /dev/random 287c478bd9Sstevel@tonic-gate * driver uses for read(2), write(2) and poll(2) on /dev/random or 297c478bd9Sstevel@tonic-gate * /dev/urandom. It also implements the kernel API - random_add_entropy(), 303364c169SVladimir Kotal * random_add_pseudo_entropy(), random_get_pseudo_bytes() 313364c169SVladimir Kotal * and random_get_bytes(). 327c478bd9Sstevel@tonic-gate * 337c478bd9Sstevel@tonic-gate * We periodically collect random bits from providers which are registered 347c478bd9Sstevel@tonic-gate * with the Kernel Cryptographic Framework (kCF) as capable of random 357c478bd9Sstevel@tonic-gate * number generation. The random bits are maintained in a cache and 367c478bd9Sstevel@tonic-gate * it is used for high quality random numbers (/dev/random) requests. 377c478bd9Sstevel@tonic-gate * We pick a provider and call its SPI routine, if the cache does not have 387c478bd9Sstevel@tonic-gate * enough bytes to satisfy a request. 397c478bd9Sstevel@tonic-gate * 407c478bd9Sstevel@tonic-gate * /dev/urandom requests use a software-based generator algorithm that uses the 417c478bd9Sstevel@tonic-gate * random bits in the cache as a seed. We create one pseudo-random generator 427c478bd9Sstevel@tonic-gate * (for /dev/urandom) per possible CPU on the system, and use it, 437c478bd9Sstevel@tonic-gate * kmem-magazine-style, to avoid cache line contention. 447c478bd9Sstevel@tonic-gate * 457c478bd9Sstevel@tonic-gate * LOCKING HIERARCHY: 46fe54a78eSHai-May Chao * 1) rmp->rm_mag.rm_lock protects the per-cpu pseudo-random generators. 477c478bd9Sstevel@tonic-gate * 2) rndpool_lock protects the high-quality randomness pool. 48fe54a78eSHai-May Chao * It may be locked while a rmp->rm_mag.rm_lock is held. 497c478bd9Sstevel@tonic-gate * 507c478bd9Sstevel@tonic-gate * A history note: The kernel API and the software-based algorithms in this 517c478bd9Sstevel@tonic-gate * file used to be part of the /dev/random driver. 527c478bd9Sstevel@tonic-gate */ 537c478bd9Sstevel@tonic-gate 547c478bd9Sstevel@tonic-gate #include <sys/types.h> 557c478bd9Sstevel@tonic-gate #include <sys/conf.h> 567c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 577c478bd9Sstevel@tonic-gate #include <sys/disp.h> 587c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 597c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 607c478bd9Sstevel@tonic-gate #include <sys/crypto/common.h> 617c478bd9Sstevel@tonic-gate #include <sys/crypto/api.h> 627c478bd9Sstevel@tonic-gate #include <sys/crypto/impl.h> 637c478bd9Sstevel@tonic-gate #include <sys/crypto/sched_impl.h> 64*73556491SAnthony Scarpino #include <sys/crypto/ioctladmin.h> 657c478bd9Sstevel@tonic-gate #include <sys/random.h> 667c478bd9Sstevel@tonic-gate #include <sys/sha1.h> 677c478bd9Sstevel@tonic-gate #include <sys/time.h> 687c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 697c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h> 707c478bd9Sstevel@tonic-gate #include <sys/taskq.h> 71fe54a78eSHai-May Chao #include <rng/fips_random.h> 727c478bd9Sstevel@tonic-gate 737c478bd9Sstevel@tonic-gate #define RNDPOOLSIZE 1024 /* Pool size in bytes */ 747c478bd9Sstevel@tonic-gate #define MINEXTRACTBYTES 20 757c478bd9Sstevel@tonic-gate #define MAXEXTRACTBYTES 1024 767c478bd9Sstevel@tonic-gate #define PRNG_MAXOBLOCKS 1310720 /* Max output block per prng key */ 777c478bd9Sstevel@tonic-gate #define TIMEOUT_INTERVAL 5 /* Periodic mixing interval in secs */ 787c478bd9Sstevel@tonic-gate 797c478bd9Sstevel@tonic-gate typedef enum extract_type { 807c478bd9Sstevel@tonic-gate NONBLOCK_EXTRACT, 817c478bd9Sstevel@tonic-gate BLOCKING_EXTRACT, 827c478bd9Sstevel@tonic-gate ALWAYS_EXTRACT 837c478bd9Sstevel@tonic-gate } extract_type_t; 847c478bd9Sstevel@tonic-gate 857c478bd9Sstevel@tonic-gate /* 867c478bd9Sstevel@tonic-gate * Hash-algo generic definitions. For now, they are SHA1's. We use SHA1 877c478bd9Sstevel@tonic-gate * routines directly instead of using k-API because we can't return any 887c478bd9Sstevel@tonic-gate * error code in /dev/urandom case and we can get an error using k-API 897c478bd9Sstevel@tonic-gate * if a mechanism is disabled. 907c478bd9Sstevel@tonic-gate */ 917c478bd9Sstevel@tonic-gate #define HASHSIZE 20 927c478bd9Sstevel@tonic-gate #define HASH_CTX SHA1_CTX 937c478bd9Sstevel@tonic-gate #define HashInit(ctx) SHA1Init((ctx)) 947c478bd9Sstevel@tonic-gate #define HashUpdate(ctx, p, s) SHA1Update((ctx), (p), (s)) 957c478bd9Sstevel@tonic-gate #define HashFinal(d, ctx) SHA1Final((d), (ctx)) 967c478bd9Sstevel@tonic-gate 977c478bd9Sstevel@tonic-gate /* HMAC-SHA1 */ 987c478bd9Sstevel@tonic-gate #define HMAC_KEYSIZE 20 997c478bd9Sstevel@tonic-gate 1007c478bd9Sstevel@tonic-gate /* 1017c478bd9Sstevel@tonic-gate * Cache of random bytes implemented as a circular buffer. findex and rindex 1027c478bd9Sstevel@tonic-gate * track the front and back of the circular buffer. 1037c478bd9Sstevel@tonic-gate */ 1047c478bd9Sstevel@tonic-gate uint8_t rndpool[RNDPOOLSIZE]; 1057c478bd9Sstevel@tonic-gate static int findex, rindex; 1067c478bd9Sstevel@tonic-gate static int rnbyte_cnt; /* Number of bytes in the cache */ 1077c478bd9Sstevel@tonic-gate 1087c478bd9Sstevel@tonic-gate static kmutex_t rndpool_lock; /* protects r/w accesses to the cache, */ 1097c478bd9Sstevel@tonic-gate /* and the global variables */ 1107c478bd9Sstevel@tonic-gate static kcondvar_t rndpool_read_cv; /* serializes poll/read syscalls */ 1117c478bd9Sstevel@tonic-gate static int num_waiters; /* #threads waiting to read from /dev/random */ 1127c478bd9Sstevel@tonic-gate 1137c478bd9Sstevel@tonic-gate static struct pollhead rnd_pollhead; 114104d3bdeSDan OpenSolaris Anderson /* LINTED E_STATIC_UNUSED */ 1157c478bd9Sstevel@tonic-gate static timeout_id_t kcf_rndtimeout_id; 1167c478bd9Sstevel@tonic-gate static crypto_mech_type_t rngmech_type = CRYPTO_MECH_INVALID; 1177c478bd9Sstevel@tonic-gate rnd_stats_t rnd_stats; 118f317a3a3Skrishna static boolean_t rng_prov_found = B_TRUE; 119f317a3a3Skrishna static boolean_t rng_ok_to_log = B_TRUE; 120f5229b7eSKrishna Yenduri static boolean_t rngprov_task_idle = B_TRUE; 1217c478bd9Sstevel@tonic-gate 1227c478bd9Sstevel@tonic-gate static void rndc_addbytes(uint8_t *, size_t); 1237c478bd9Sstevel@tonic-gate static void rndc_getbytes(uint8_t *ptr, size_t len); 1247c478bd9Sstevel@tonic-gate static void rnd_handler(void *); 1257c478bd9Sstevel@tonic-gate static void rnd_alloc_magazines(); 1267c478bd9Sstevel@tonic-gate 1277c478bd9Sstevel@tonic-gate void 1287c478bd9Sstevel@tonic-gate kcf_rnd_init() 1297c478bd9Sstevel@tonic-gate { 1307c478bd9Sstevel@tonic-gate hrtime_t ts; 1317c478bd9Sstevel@tonic-gate time_t now; 1327c478bd9Sstevel@tonic-gate 1337c478bd9Sstevel@tonic-gate mutex_init(&rndpool_lock, NULL, MUTEX_DEFAULT, NULL); 1347c478bd9Sstevel@tonic-gate cv_init(&rndpool_read_cv, NULL, CV_DEFAULT, NULL); 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate /* 1377c478bd9Sstevel@tonic-gate * Add bytes to the cache using 1387c478bd9Sstevel@tonic-gate * . 2 unpredictable times: high resolution time since the boot-time, 1397c478bd9Sstevel@tonic-gate * and the current time-of-the day. 1407c478bd9Sstevel@tonic-gate * This is used only to make the timeout value in the timer 1417c478bd9Sstevel@tonic-gate * unpredictable. 1427c478bd9Sstevel@tonic-gate */ 1437c478bd9Sstevel@tonic-gate ts = gethrtime(); 1447c478bd9Sstevel@tonic-gate rndc_addbytes((uint8_t *)&ts, sizeof (ts)); 1457c478bd9Sstevel@tonic-gate 1467c478bd9Sstevel@tonic-gate (void) drv_getparm(TIME, &now); 1477c478bd9Sstevel@tonic-gate rndc_addbytes((uint8_t *)&now, sizeof (now)); 1487c478bd9Sstevel@tonic-gate 1497c478bd9Sstevel@tonic-gate rnbyte_cnt = 0; 1507c478bd9Sstevel@tonic-gate findex = rindex = 0; 1517c478bd9Sstevel@tonic-gate num_waiters = 0; 1527c478bd9Sstevel@tonic-gate rngmech_type = KCF_MECHID(KCF_MISC_CLASS, 0); 1537c478bd9Sstevel@tonic-gate 1547c478bd9Sstevel@tonic-gate rnd_alloc_magazines(); 1557c478bd9Sstevel@tonic-gate } 1567c478bd9Sstevel@tonic-gate 1577c478bd9Sstevel@tonic-gate /* 1587c478bd9Sstevel@tonic-gate * Return TRUE if at least one provider exists that can 1597c478bd9Sstevel@tonic-gate * supply random numbers. 1607c478bd9Sstevel@tonic-gate */ 1617c478bd9Sstevel@tonic-gate boolean_t 1627c478bd9Sstevel@tonic-gate kcf_rngprov_check(void) 1637c478bd9Sstevel@tonic-gate { 1647c478bd9Sstevel@tonic-gate int rv; 1657c478bd9Sstevel@tonic-gate kcf_provider_desc_t *pd; 1667c478bd9Sstevel@tonic-gate 167436935a1SVladimir Kotal if ((pd = kcf_get_mech_provider(rngmech_type, NULL, NULL, &rv, 1687c478bd9Sstevel@tonic-gate NULL, CRYPTO_FG_RANDOM, B_FALSE, 0)) != NULL) { 1697c478bd9Sstevel@tonic-gate KCF_PROV_REFRELE(pd); 170f317a3a3Skrishna /* 171f317a3a3Skrishna * We logged a warning once about no provider being available 172f317a3a3Skrishna * and now a provider became available. So, set the flag so 173f317a3a3Skrishna * that we can log again if the problem recurs. 174f317a3a3Skrishna */ 175f317a3a3Skrishna rng_ok_to_log = B_TRUE; 176f317a3a3Skrishna rng_prov_found = B_TRUE; 1777c478bd9Sstevel@tonic-gate return (B_TRUE); 178f317a3a3Skrishna } else { 179f317a3a3Skrishna rng_prov_found = B_FALSE; 1807c478bd9Sstevel@tonic-gate return (B_FALSE); 181f317a3a3Skrishna } 1827c478bd9Sstevel@tonic-gate } 1837c478bd9Sstevel@tonic-gate 1847c478bd9Sstevel@tonic-gate /* 1857c478bd9Sstevel@tonic-gate * Pick a software-based provider and submit a request to seed 1867c478bd9Sstevel@tonic-gate * its random number generator. 1877c478bd9Sstevel@tonic-gate */ 1887c478bd9Sstevel@tonic-gate static void 1898047c9fbSmcpowers rngprov_seed(uint8_t *buf, int len, uint_t entropy_est, uint32_t flags) 1907c478bd9Sstevel@tonic-gate { 1917c478bd9Sstevel@tonic-gate kcf_provider_desc_t *pd = NULL; 1927c478bd9Sstevel@tonic-gate 1936a1073f8Skrishna if (kcf_get_sw_prov(rngmech_type, &pd, NULL, B_FALSE) == 1946a1073f8Skrishna CRYPTO_SUCCESS) { 1958047c9fbSmcpowers (void) KCF_PROV_SEED_RANDOM(pd, pd->pd_sid, buf, len, 1968047c9fbSmcpowers entropy_est, flags, NULL); 1977c478bd9Sstevel@tonic-gate KCF_PROV_REFRELE(pd); 1987c478bd9Sstevel@tonic-gate } 1997c478bd9Sstevel@tonic-gate } 2007c478bd9Sstevel@tonic-gate 2017c478bd9Sstevel@tonic-gate /* 2027c478bd9Sstevel@tonic-gate * This routine is called for blocking reads. 2037c478bd9Sstevel@tonic-gate * 2047c478bd9Sstevel@tonic-gate * The argument is_taskq_thr indicates whether the caller is 2057c478bd9Sstevel@tonic-gate * the taskq thread dispatched by the timeout handler routine. 2067c478bd9Sstevel@tonic-gate * In this case, we cycle through all the providers 2077c478bd9Sstevel@tonic-gate * submitting a request to each provider to generate random numbers. 2087c478bd9Sstevel@tonic-gate * 2097c478bd9Sstevel@tonic-gate * For other cases, we pick a provider and submit a request to generate 2107c478bd9Sstevel@tonic-gate * random numbers. We retry using another provider if we get an error. 2117c478bd9Sstevel@tonic-gate * 2127c478bd9Sstevel@tonic-gate * Returns the number of bytes that are written to 'ptr'. Returns -1 2137c478bd9Sstevel@tonic-gate * if no provider is found. ptr and need are unchanged. 2147c478bd9Sstevel@tonic-gate */ 2157c478bd9Sstevel@tonic-gate static int 2168b502715SKrishna Yenduri rngprov_getbytes(uint8_t *ptr, size_t need, boolean_t is_taskq_thr) 2177c478bd9Sstevel@tonic-gate { 2187c478bd9Sstevel@tonic-gate int rv; 2197c478bd9Sstevel@tonic-gate int prov_cnt = 0; 2207c478bd9Sstevel@tonic-gate int total_bytes = 0; 2217c478bd9Sstevel@tonic-gate kcf_provider_desc_t *pd; 2227c478bd9Sstevel@tonic-gate kcf_req_params_t params; 2237c478bd9Sstevel@tonic-gate kcf_prov_tried_t *list = NULL; 2247c478bd9Sstevel@tonic-gate 225436935a1SVladimir Kotal while ((pd = kcf_get_mech_provider(rngmech_type, NULL, NULL, &rv, 2267c478bd9Sstevel@tonic-gate list, CRYPTO_FG_RANDOM, B_FALSE, 0)) != NULL) { 2277c478bd9Sstevel@tonic-gate 2287c478bd9Sstevel@tonic-gate prov_cnt++; 2297c478bd9Sstevel@tonic-gate 2307c478bd9Sstevel@tonic-gate KCF_WRAP_RANDOM_OPS_PARAMS(¶ms, KCF_OP_RANDOM_GENERATE, 2318047c9fbSmcpowers pd->pd_sid, ptr, need, 0, 0); 2327c478bd9Sstevel@tonic-gate rv = kcf_submit_request(pd, NULL, NULL, ¶ms, B_FALSE); 2337c478bd9Sstevel@tonic-gate ASSERT(rv != CRYPTO_QUEUED); 2347c478bd9Sstevel@tonic-gate 2357c478bd9Sstevel@tonic-gate if (rv == CRYPTO_SUCCESS) { 2367c478bd9Sstevel@tonic-gate total_bytes += need; 2377c478bd9Sstevel@tonic-gate if (is_taskq_thr) 2387c478bd9Sstevel@tonic-gate rndc_addbytes(ptr, need); 2397c478bd9Sstevel@tonic-gate else { 2407c478bd9Sstevel@tonic-gate KCF_PROV_REFRELE(pd); 2417c478bd9Sstevel@tonic-gate break; 2427c478bd9Sstevel@tonic-gate } 2437c478bd9Sstevel@tonic-gate } 2447c478bd9Sstevel@tonic-gate 2457c478bd9Sstevel@tonic-gate if (is_taskq_thr || rv != CRYPTO_SUCCESS) { 2467c478bd9Sstevel@tonic-gate /* Add pd to the linked list of providers tried. */ 2477c478bd9Sstevel@tonic-gate if (kcf_insert_triedlist(&list, pd, KM_SLEEP) == NULL) { 2487c478bd9Sstevel@tonic-gate KCF_PROV_REFRELE(pd); 2497c478bd9Sstevel@tonic-gate break; 2507c478bd9Sstevel@tonic-gate } 2517c478bd9Sstevel@tonic-gate } 2527c478bd9Sstevel@tonic-gate 2537c478bd9Sstevel@tonic-gate } 2547c478bd9Sstevel@tonic-gate 2557c478bd9Sstevel@tonic-gate if (list != NULL) 2567c478bd9Sstevel@tonic-gate kcf_free_triedlist(list); 2577c478bd9Sstevel@tonic-gate 2587c478bd9Sstevel@tonic-gate if (prov_cnt == 0) { /* no provider could be found. */ 259f317a3a3Skrishna rng_prov_found = B_FALSE; 2607c478bd9Sstevel@tonic-gate return (-1); 261f317a3a3Skrishna } else { 262f317a3a3Skrishna rng_prov_found = B_TRUE; 263f317a3a3Skrishna /* See comments in kcf_rngprov_check() */ 264f317a3a3Skrishna rng_ok_to_log = B_TRUE; 2657c478bd9Sstevel@tonic-gate } 2667c478bd9Sstevel@tonic-gate 2677c478bd9Sstevel@tonic-gate return (total_bytes); 2687c478bd9Sstevel@tonic-gate } 2697c478bd9Sstevel@tonic-gate 2707c478bd9Sstevel@tonic-gate static void 2717c478bd9Sstevel@tonic-gate notify_done(void *arg, int rv) 2727c478bd9Sstevel@tonic-gate { 2737c478bd9Sstevel@tonic-gate uchar_t *rndbuf = arg; 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate if (rv == CRYPTO_SUCCESS) 2767c478bd9Sstevel@tonic-gate rndc_addbytes(rndbuf, MINEXTRACTBYTES); 2777c478bd9Sstevel@tonic-gate 2787c478bd9Sstevel@tonic-gate bzero(rndbuf, MINEXTRACTBYTES); 2797c478bd9Sstevel@tonic-gate kmem_free(rndbuf, MINEXTRACTBYTES); 2807c478bd9Sstevel@tonic-gate } 2817c478bd9Sstevel@tonic-gate 2827c478bd9Sstevel@tonic-gate /* 2837c478bd9Sstevel@tonic-gate * Cycle through all the providers submitting a request to each provider 2847c478bd9Sstevel@tonic-gate * to generate random numbers. This is called for the modes - NONBLOCK_EXTRACT 2857c478bd9Sstevel@tonic-gate * and ALWAYS_EXTRACT. 2867c478bd9Sstevel@tonic-gate * 2877c478bd9Sstevel@tonic-gate * Returns the number of bytes that are written to 'ptr'. Returns -1 2887c478bd9Sstevel@tonic-gate * if no provider is found. ptr and len are unchanged. 2897c478bd9Sstevel@tonic-gate */ 2907c478bd9Sstevel@tonic-gate static int 2918b502715SKrishna Yenduri rngprov_getbytes_nblk(uint8_t *ptr, size_t len) 2927c478bd9Sstevel@tonic-gate { 293104d3bdeSDan OpenSolaris Anderson int rv, total_bytes; 294104d3bdeSDan OpenSolaris Anderson size_t blen; 2957c478bd9Sstevel@tonic-gate uchar_t *rndbuf; 2967c478bd9Sstevel@tonic-gate kcf_provider_desc_t *pd; 2977c478bd9Sstevel@tonic-gate kcf_req_params_t params; 2987c478bd9Sstevel@tonic-gate crypto_call_req_t req; 2997c478bd9Sstevel@tonic-gate kcf_prov_tried_t *list = NULL; 3007c478bd9Sstevel@tonic-gate int prov_cnt = 0; 3017c478bd9Sstevel@tonic-gate 3027c478bd9Sstevel@tonic-gate blen = 0; 3037c478bd9Sstevel@tonic-gate total_bytes = 0; 3047c478bd9Sstevel@tonic-gate req.cr_flag = CRYPTO_SKIP_REQID; 3057c478bd9Sstevel@tonic-gate req.cr_callback_func = notify_done; 3067c478bd9Sstevel@tonic-gate 307436935a1SVladimir Kotal while ((pd = kcf_get_mech_provider(rngmech_type, NULL, NULL, &rv, 3087c478bd9Sstevel@tonic-gate list, CRYPTO_FG_RANDOM, CHECK_RESTRICT(&req), 0)) != NULL) { 3097c478bd9Sstevel@tonic-gate 3107c478bd9Sstevel@tonic-gate prov_cnt ++; 3117c478bd9Sstevel@tonic-gate switch (pd->pd_prov_type) { 3127c478bd9Sstevel@tonic-gate case CRYPTO_HW_PROVIDER: 3137c478bd9Sstevel@tonic-gate /* 3147c478bd9Sstevel@tonic-gate * We have to allocate a buffer here as we can not 3157c478bd9Sstevel@tonic-gate * assume that the input buffer will remain valid 3167c478bd9Sstevel@tonic-gate * when the callback comes. We use a fixed size buffer 3177c478bd9Sstevel@tonic-gate * to simplify the book keeping. 3187c478bd9Sstevel@tonic-gate */ 3197c478bd9Sstevel@tonic-gate rndbuf = kmem_alloc(MINEXTRACTBYTES, KM_NOSLEEP); 3207c478bd9Sstevel@tonic-gate if (rndbuf == NULL) { 3217c478bd9Sstevel@tonic-gate KCF_PROV_REFRELE(pd); 3227c478bd9Sstevel@tonic-gate if (list != NULL) 3237c478bd9Sstevel@tonic-gate kcf_free_triedlist(list); 3247c478bd9Sstevel@tonic-gate return (total_bytes); 3257c478bd9Sstevel@tonic-gate } 3267c478bd9Sstevel@tonic-gate req.cr_callback_arg = rndbuf; 3277c478bd9Sstevel@tonic-gate KCF_WRAP_RANDOM_OPS_PARAMS(¶ms, 3287c478bd9Sstevel@tonic-gate KCF_OP_RANDOM_GENERATE, 3298047c9fbSmcpowers pd->pd_sid, rndbuf, MINEXTRACTBYTES, 0, 0); 3307c478bd9Sstevel@tonic-gate break; 3317c478bd9Sstevel@tonic-gate 3327c478bd9Sstevel@tonic-gate case CRYPTO_SW_PROVIDER: 3337c478bd9Sstevel@tonic-gate /* 3347c478bd9Sstevel@tonic-gate * We do not need to allocate a buffer in the software 3357c478bd9Sstevel@tonic-gate * provider case as there is no callback involved. We 3367c478bd9Sstevel@tonic-gate * avoid any extra data copy by directly passing 'ptr'. 3377c478bd9Sstevel@tonic-gate */ 3387c478bd9Sstevel@tonic-gate KCF_WRAP_RANDOM_OPS_PARAMS(¶ms, 3397c478bd9Sstevel@tonic-gate KCF_OP_RANDOM_GENERATE, 3408047c9fbSmcpowers pd->pd_sid, ptr, len, 0, 0); 3417c478bd9Sstevel@tonic-gate break; 3427c478bd9Sstevel@tonic-gate } 3437c478bd9Sstevel@tonic-gate 3447c478bd9Sstevel@tonic-gate rv = kcf_submit_request(pd, NULL, &req, ¶ms, B_FALSE); 3457c478bd9Sstevel@tonic-gate if (rv == CRYPTO_SUCCESS) { 3467c478bd9Sstevel@tonic-gate switch (pd->pd_prov_type) { 3477c478bd9Sstevel@tonic-gate case CRYPTO_HW_PROVIDER: 3487c478bd9Sstevel@tonic-gate /* 3497c478bd9Sstevel@tonic-gate * Since we have the input buffer handy, 3507c478bd9Sstevel@tonic-gate * we directly copy to it rather than 3517c478bd9Sstevel@tonic-gate * adding to the pool. 3527c478bd9Sstevel@tonic-gate */ 3537c478bd9Sstevel@tonic-gate blen = min(MINEXTRACTBYTES, len); 3547c478bd9Sstevel@tonic-gate bcopy(rndbuf, ptr, blen); 3557c478bd9Sstevel@tonic-gate if (len < MINEXTRACTBYTES) 3567c478bd9Sstevel@tonic-gate rndc_addbytes(rndbuf + len, 3577c478bd9Sstevel@tonic-gate MINEXTRACTBYTES - len); 3587c478bd9Sstevel@tonic-gate ptr += blen; 3597c478bd9Sstevel@tonic-gate len -= blen; 3607c478bd9Sstevel@tonic-gate total_bytes += blen; 3617c478bd9Sstevel@tonic-gate break; 3627c478bd9Sstevel@tonic-gate 3637c478bd9Sstevel@tonic-gate case CRYPTO_SW_PROVIDER: 3647c478bd9Sstevel@tonic-gate total_bytes += len; 3657c478bd9Sstevel@tonic-gate len = 0; 3667c478bd9Sstevel@tonic-gate break; 3677c478bd9Sstevel@tonic-gate } 3687c478bd9Sstevel@tonic-gate } 3697c478bd9Sstevel@tonic-gate 3707c478bd9Sstevel@tonic-gate /* 3717c478bd9Sstevel@tonic-gate * We free the buffer in the callback routine 3727c478bd9Sstevel@tonic-gate * for the CRYPTO_QUEUED case. 3737c478bd9Sstevel@tonic-gate */ 3747c478bd9Sstevel@tonic-gate if (pd->pd_prov_type == CRYPTO_HW_PROVIDER && 3757c478bd9Sstevel@tonic-gate rv != CRYPTO_QUEUED) { 3767c478bd9Sstevel@tonic-gate bzero(rndbuf, MINEXTRACTBYTES); 3777c478bd9Sstevel@tonic-gate kmem_free(rndbuf, MINEXTRACTBYTES); 3787c478bd9Sstevel@tonic-gate } 3797c478bd9Sstevel@tonic-gate 3807c478bd9Sstevel@tonic-gate if (len == 0) { 3817c478bd9Sstevel@tonic-gate KCF_PROV_REFRELE(pd); 3827c478bd9Sstevel@tonic-gate break; 3837c478bd9Sstevel@tonic-gate } 3847c478bd9Sstevel@tonic-gate 3857c478bd9Sstevel@tonic-gate if (rv != CRYPTO_SUCCESS) { 3867c478bd9Sstevel@tonic-gate /* Add pd to the linked list of providers tried. */ 3877c478bd9Sstevel@tonic-gate if (kcf_insert_triedlist(&list, pd, KM_NOSLEEP) == 3887c478bd9Sstevel@tonic-gate NULL) { 3897c478bd9Sstevel@tonic-gate KCF_PROV_REFRELE(pd); 3907c478bd9Sstevel@tonic-gate break; 3917c478bd9Sstevel@tonic-gate } 3927c478bd9Sstevel@tonic-gate } 3937c478bd9Sstevel@tonic-gate } 3947c478bd9Sstevel@tonic-gate 3957c478bd9Sstevel@tonic-gate if (list != NULL) { 3967c478bd9Sstevel@tonic-gate kcf_free_triedlist(list); 3977c478bd9Sstevel@tonic-gate } 3987c478bd9Sstevel@tonic-gate 3997c478bd9Sstevel@tonic-gate if (prov_cnt == 0) { /* no provider could be found. */ 400f317a3a3Skrishna rng_prov_found = B_FALSE; 4017c478bd9Sstevel@tonic-gate return (-1); 402f317a3a3Skrishna } else { 403f317a3a3Skrishna rng_prov_found = B_TRUE; 404f317a3a3Skrishna /* See comments in kcf_rngprov_check() */ 405f317a3a3Skrishna rng_ok_to_log = B_TRUE; 4067c478bd9Sstevel@tonic-gate } 4077c478bd9Sstevel@tonic-gate 4087c478bd9Sstevel@tonic-gate return (total_bytes); 4097c478bd9Sstevel@tonic-gate } 4107c478bd9Sstevel@tonic-gate 4117c478bd9Sstevel@tonic-gate static void 4127c478bd9Sstevel@tonic-gate rngprov_task(void *arg) 4137c478bd9Sstevel@tonic-gate { 4147c478bd9Sstevel@tonic-gate int len = (int)(uintptr_t)arg; 4157c478bd9Sstevel@tonic-gate uchar_t tbuf[MAXEXTRACTBYTES]; 4167c478bd9Sstevel@tonic-gate 4177c478bd9Sstevel@tonic-gate ASSERT(len <= MAXEXTRACTBYTES); 4188b502715SKrishna Yenduri (void) rngprov_getbytes(tbuf, len, B_TRUE); 419f5229b7eSKrishna Yenduri rngprov_task_idle = B_TRUE; 4207c478bd9Sstevel@tonic-gate } 4217c478bd9Sstevel@tonic-gate 4227c478bd9Sstevel@tonic-gate /* 4237c478bd9Sstevel@tonic-gate * Returns "len" random or pseudo-random bytes in *ptr. 4247c478bd9Sstevel@tonic-gate * Will block if not enough random bytes are available and the 4257c478bd9Sstevel@tonic-gate * call is blocking. 4267c478bd9Sstevel@tonic-gate * 4277c478bd9Sstevel@tonic-gate * Called with rndpool_lock held (allowing caller to do optimistic locking; 4287c478bd9Sstevel@tonic-gate * releases the lock before return). 4297c478bd9Sstevel@tonic-gate */ 4307c478bd9Sstevel@tonic-gate static int 4318b502715SKrishna Yenduri rnd_get_bytes(uint8_t *ptr, size_t len, extract_type_t how) 4327c478bd9Sstevel@tonic-gate { 433104d3bdeSDan OpenSolaris Anderson size_t bytes; 4347c478bd9Sstevel@tonic-gate size_t got; 4357c478bd9Sstevel@tonic-gate 4367c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&rndpool_lock)); 4377c478bd9Sstevel@tonic-gate /* 4387c478bd9Sstevel@tonic-gate * Check if the request can be satisfied from the cache 4397c478bd9Sstevel@tonic-gate * of random bytes. 4407c478bd9Sstevel@tonic-gate */ 4417c478bd9Sstevel@tonic-gate if (len <= rnbyte_cnt) { 4427c478bd9Sstevel@tonic-gate rndc_getbytes(ptr, len); 4437c478bd9Sstevel@tonic-gate mutex_exit(&rndpool_lock); 4447c478bd9Sstevel@tonic-gate return (0); 4457c478bd9Sstevel@tonic-gate } 4467c478bd9Sstevel@tonic-gate mutex_exit(&rndpool_lock); 4477c478bd9Sstevel@tonic-gate 4487c478bd9Sstevel@tonic-gate switch (how) { 4497c478bd9Sstevel@tonic-gate case BLOCKING_EXTRACT: 4508b502715SKrishna Yenduri if ((got = rngprov_getbytes(ptr, len, B_FALSE)) == -1) 4517c478bd9Sstevel@tonic-gate break; /* No provider found */ 4527c478bd9Sstevel@tonic-gate 4537c478bd9Sstevel@tonic-gate if (got == len) 4547c478bd9Sstevel@tonic-gate return (0); 4557c478bd9Sstevel@tonic-gate len -= got; 4567c478bd9Sstevel@tonic-gate ptr += got; 4577c478bd9Sstevel@tonic-gate break; 4587c478bd9Sstevel@tonic-gate 4597c478bd9Sstevel@tonic-gate case NONBLOCK_EXTRACT: 4607c478bd9Sstevel@tonic-gate case ALWAYS_EXTRACT: 4618b502715SKrishna Yenduri if ((got = rngprov_getbytes_nblk(ptr, len)) == -1) { 4627c478bd9Sstevel@tonic-gate /* No provider found */ 4637c478bd9Sstevel@tonic-gate if (how == NONBLOCK_EXTRACT) { 4647c478bd9Sstevel@tonic-gate return (EAGAIN); 4657c478bd9Sstevel@tonic-gate } 4667c478bd9Sstevel@tonic-gate } else { 4677c478bd9Sstevel@tonic-gate if (got == len) 4687c478bd9Sstevel@tonic-gate return (0); 4697c478bd9Sstevel@tonic-gate len -= got; 4707c478bd9Sstevel@tonic-gate ptr += got; 4717c478bd9Sstevel@tonic-gate } 4727c478bd9Sstevel@tonic-gate if (how == NONBLOCK_EXTRACT && (rnbyte_cnt < len)) 4737c478bd9Sstevel@tonic-gate return (EAGAIN); 4747c478bd9Sstevel@tonic-gate break; 4757c478bd9Sstevel@tonic-gate } 4767c478bd9Sstevel@tonic-gate 4777c478bd9Sstevel@tonic-gate mutex_enter(&rndpool_lock); 4787c478bd9Sstevel@tonic-gate while (len > 0) { 4797c478bd9Sstevel@tonic-gate if (how == BLOCKING_EXTRACT) { 4807c478bd9Sstevel@tonic-gate /* Check if there is enough */ 4817c478bd9Sstevel@tonic-gate while (rnbyte_cnt < MINEXTRACTBYTES) { 4827c478bd9Sstevel@tonic-gate num_waiters++; 4837c478bd9Sstevel@tonic-gate if (cv_wait_sig(&rndpool_read_cv, 4847c478bd9Sstevel@tonic-gate &rndpool_lock) == 0) { 4857c478bd9Sstevel@tonic-gate num_waiters--; 4867c478bd9Sstevel@tonic-gate mutex_exit(&rndpool_lock); 4877c478bd9Sstevel@tonic-gate return (EINTR); 4887c478bd9Sstevel@tonic-gate } 4897c478bd9Sstevel@tonic-gate num_waiters--; 4907c478bd9Sstevel@tonic-gate } 4917c478bd9Sstevel@tonic-gate } 4927c478bd9Sstevel@tonic-gate 4937c478bd9Sstevel@tonic-gate /* Figure out how many bytes to extract */ 4947c478bd9Sstevel@tonic-gate bytes = min(len, rnbyte_cnt); 4957c478bd9Sstevel@tonic-gate rndc_getbytes(ptr, bytes); 4967c478bd9Sstevel@tonic-gate 4977c478bd9Sstevel@tonic-gate len -= bytes; 4987c478bd9Sstevel@tonic-gate ptr += bytes; 4997c478bd9Sstevel@tonic-gate 5007c478bd9Sstevel@tonic-gate if (len > 0 && how == ALWAYS_EXTRACT) { 5017c478bd9Sstevel@tonic-gate /* 5027c478bd9Sstevel@tonic-gate * There are not enough bytes, but we can not block. 5037c478bd9Sstevel@tonic-gate * This only happens in the case of /dev/urandom which 5047c478bd9Sstevel@tonic-gate * runs an additional generation algorithm. So, there 5057c478bd9Sstevel@tonic-gate * is no problem. 5067c478bd9Sstevel@tonic-gate */ 5077c478bd9Sstevel@tonic-gate while (len > 0) { 5087c478bd9Sstevel@tonic-gate *ptr = rndpool[findex]; 5097c478bd9Sstevel@tonic-gate ptr++; len--; 5107c478bd9Sstevel@tonic-gate rindex = findex = (findex + 1) & 5117c478bd9Sstevel@tonic-gate (RNDPOOLSIZE - 1); 5127c478bd9Sstevel@tonic-gate } 5137c478bd9Sstevel@tonic-gate break; 5147c478bd9Sstevel@tonic-gate } 5157c478bd9Sstevel@tonic-gate } 5167c478bd9Sstevel@tonic-gate 5177c478bd9Sstevel@tonic-gate mutex_exit(&rndpool_lock); 5187c478bd9Sstevel@tonic-gate return (0); 5197c478bd9Sstevel@tonic-gate } 5207c478bd9Sstevel@tonic-gate 5217c478bd9Sstevel@tonic-gate int 5228b502715SKrishna Yenduri kcf_rnd_get_bytes(uint8_t *ptr, size_t len, boolean_t noblock) 5237c478bd9Sstevel@tonic-gate { 5247c478bd9Sstevel@tonic-gate extract_type_t how; 5257c478bd9Sstevel@tonic-gate int error; 5267c478bd9Sstevel@tonic-gate 5277c478bd9Sstevel@tonic-gate how = noblock ? NONBLOCK_EXTRACT : BLOCKING_EXTRACT; 5287c478bd9Sstevel@tonic-gate mutex_enter(&rndpool_lock); 5298b502715SKrishna Yenduri if ((error = rnd_get_bytes(ptr, len, how)) != 0) 5307c478bd9Sstevel@tonic-gate return (error); 5317c478bd9Sstevel@tonic-gate 5327c478bd9Sstevel@tonic-gate BUMP_RND_STATS(rs_rndOut, len); 5337c478bd9Sstevel@tonic-gate return (0); 5347c478bd9Sstevel@tonic-gate } 5357c478bd9Sstevel@tonic-gate 5367c478bd9Sstevel@tonic-gate /* 5377c478bd9Sstevel@tonic-gate * Revisit this if the structs grow or we come up with a better way 5387c478bd9Sstevel@tonic-gate * of cache-line-padding structures. 5397c478bd9Sstevel@tonic-gate */ 5407c478bd9Sstevel@tonic-gate #define RND_CPU_CACHE_SIZE 64 541fe54a78eSHai-May Chao #define RND_CPU_PAD_SIZE RND_CPU_CACHE_SIZE*6 5427c478bd9Sstevel@tonic-gate #define RND_CPU_PAD (RND_CPU_PAD_SIZE - \ 543fe54a78eSHai-May Chao sizeof (rndmag_t)) 5447c478bd9Sstevel@tonic-gate /* 5457c478bd9Sstevel@tonic-gate * Per-CPU random state. Somewhat like like kmem's magazines, this provides 5467c478bd9Sstevel@tonic-gate * a per-CPU instance of the pseudo-random generator. We have it much easier 5477c478bd9Sstevel@tonic-gate * than kmem, as we can afford to "leak" random bits if a CPU is DR'ed out. 5487c478bd9Sstevel@tonic-gate * 5497c478bd9Sstevel@tonic-gate * Note that this usage is preemption-safe; a thread 5507c478bd9Sstevel@tonic-gate * entering a critical section remembers which generator it locked 5517c478bd9Sstevel@tonic-gate * and unlocks the same one; should it be preempted and wind up running on 5527c478bd9Sstevel@tonic-gate * a different CPU, there will be a brief period of increased contention 5537c478bd9Sstevel@tonic-gate * before it exits the critical section but nothing will melt. 5547c478bd9Sstevel@tonic-gate */ 5557c478bd9Sstevel@tonic-gate typedef struct rndmag_s 5567c478bd9Sstevel@tonic-gate { 5577c478bd9Sstevel@tonic-gate kmutex_t rm_lock; 55856498af3SHai-May Chao uint8_t *rm_buffer; /* Start of buffer */ 5597c478bd9Sstevel@tonic-gate uint8_t *rm_eptr; /* End of buffer */ 5607c478bd9Sstevel@tonic-gate uint8_t *rm_rptr; /* Current read pointer */ 5617c478bd9Sstevel@tonic-gate uint32_t rm_oblocks; /* time to rekey? */ 5627c478bd9Sstevel@tonic-gate uint32_t rm_ofuzz; /* Rekey backoff state */ 5637c478bd9Sstevel@tonic-gate uint32_t rm_olimit; /* Hard rekey limit */ 5647c478bd9Sstevel@tonic-gate rnd_stats_t rm_stats; /* Per-CPU Statistics */ 56556498af3SHai-May Chao uint32_t rm_key[HASHSIZE/BYTES_IN_WORD]; /* FIPS XKEY */ 56656498af3SHai-May Chao uint32_t rm_seed[HASHSIZE/BYTES_IN_WORD]; /* seed for rekey */ 56756498af3SHai-May Chao uint32_t rm_previous[HASHSIZE/BYTES_IN_WORD]; /* prev random */ 5687c478bd9Sstevel@tonic-gate } rndmag_t; 5697c478bd9Sstevel@tonic-gate 570fe54a78eSHai-May Chao typedef struct rndmag_pad_s 571fe54a78eSHai-May Chao { 572fe54a78eSHai-May Chao rndmag_t rm_mag; 573fe54a78eSHai-May Chao uint8_t rm_pad[RND_CPU_PAD]; 574fe54a78eSHai-May Chao } rndmag_pad_t; 575fe54a78eSHai-May Chao 5767c478bd9Sstevel@tonic-gate /* 577fe54a78eSHai-May Chao * Generate random bytes for /dev/urandom by applying the 578fe54a78eSHai-May Chao * FIPS 186-2 algorithm with a key created from bytes extracted 5797c478bd9Sstevel@tonic-gate * from the pool. A maximum of PRNG_MAXOBLOCKS output blocks 5807c478bd9Sstevel@tonic-gate * is generated before a new key is obtained. 5817c478bd9Sstevel@tonic-gate * 5827c478bd9Sstevel@tonic-gate * Note that callers to this routine are likely to assume it can't fail. 5837c478bd9Sstevel@tonic-gate * 5847c478bd9Sstevel@tonic-gate * Called with rmp locked; releases lock. 5857c478bd9Sstevel@tonic-gate */ 5867c478bd9Sstevel@tonic-gate static int 587fe54a78eSHai-May Chao rnd_generate_pseudo_bytes(rndmag_pad_t *rmp, uint8_t *ptr, size_t len) 5887c478bd9Sstevel@tonic-gate { 589104d3bdeSDan OpenSolaris Anderson size_t bytes = len, size; 590104d3bdeSDan OpenSolaris Anderson int nblock; 5917c478bd9Sstevel@tonic-gate uint32_t oblocks; 59256498af3SHai-May Chao uint32_t tempout[HASHSIZE/BYTES_IN_WORD]; 59356498af3SHai-May Chao uint32_t seed[HASHSIZE/BYTES_IN_WORD]; 594fe54a78eSHai-May Chao int i; 595fe54a78eSHai-May Chao hrtime_t timestamp; 596fe54a78eSHai-May Chao uint8_t *src, *dst; 5977c478bd9Sstevel@tonic-gate 598fe54a78eSHai-May Chao ASSERT(mutex_owned(&rmp->rm_mag.rm_lock)); 5997c478bd9Sstevel@tonic-gate 6007c478bd9Sstevel@tonic-gate /* Nothing is being asked */ 6017c478bd9Sstevel@tonic-gate if (len == 0) { 602fe54a78eSHai-May Chao mutex_exit(&rmp->rm_mag.rm_lock); 6037c478bd9Sstevel@tonic-gate return (0); 6047c478bd9Sstevel@tonic-gate } 6057c478bd9Sstevel@tonic-gate 6067c478bd9Sstevel@tonic-gate nblock = howmany(len, HASHSIZE); 6077c478bd9Sstevel@tonic-gate 608fe54a78eSHai-May Chao rmp->rm_mag.rm_oblocks += nblock; 609fe54a78eSHai-May Chao oblocks = rmp->rm_mag.rm_oblocks; 6107c478bd9Sstevel@tonic-gate 6117c478bd9Sstevel@tonic-gate do { 612fe54a78eSHai-May Chao if (oblocks >= rmp->rm_mag.rm_olimit) { 6137c478bd9Sstevel@tonic-gate 6147c478bd9Sstevel@tonic-gate /* 6157c478bd9Sstevel@tonic-gate * Contention-avoiding rekey: see if 6167c478bd9Sstevel@tonic-gate * the pool is locked, and if so, wait a bit. 6177c478bd9Sstevel@tonic-gate * Do an 'exponential back-in' to ensure we don't 6187c478bd9Sstevel@tonic-gate * run too long without rekey. 6197c478bd9Sstevel@tonic-gate */ 620fe54a78eSHai-May Chao if (rmp->rm_mag.rm_ofuzz) { 6217c478bd9Sstevel@tonic-gate /* 6227c478bd9Sstevel@tonic-gate * Decaying exponential back-in for rekey. 6237c478bd9Sstevel@tonic-gate */ 6247c478bd9Sstevel@tonic-gate if ((rnbyte_cnt < MINEXTRACTBYTES) || 6257c478bd9Sstevel@tonic-gate (!mutex_tryenter(&rndpool_lock))) { 626fe54a78eSHai-May Chao rmp->rm_mag.rm_olimit += 627fe54a78eSHai-May Chao rmp->rm_mag.rm_ofuzz; 628fe54a78eSHai-May Chao rmp->rm_mag.rm_ofuzz >>= 1; 6297c478bd9Sstevel@tonic-gate goto punt; 6307c478bd9Sstevel@tonic-gate } 6317c478bd9Sstevel@tonic-gate } else { 6327c478bd9Sstevel@tonic-gate mutex_enter(&rndpool_lock); 6337c478bd9Sstevel@tonic-gate } 6347c478bd9Sstevel@tonic-gate 6357c478bd9Sstevel@tonic-gate /* Get a new chunk of entropy */ 636fe54a78eSHai-May Chao (void) rnd_get_bytes((uint8_t *)rmp->rm_mag.rm_key, 6378b502715SKrishna Yenduri HMAC_KEYSIZE, ALWAYS_EXTRACT); 638fe54a78eSHai-May Chao 639fe54a78eSHai-May Chao rmp->rm_mag.rm_olimit = PRNG_MAXOBLOCKS/2; 640fe54a78eSHai-May Chao rmp->rm_mag.rm_ofuzz = PRNG_MAXOBLOCKS/4; 6417c478bd9Sstevel@tonic-gate oblocks = 0; 642fe54a78eSHai-May Chao rmp->rm_mag.rm_oblocks = nblock; 6437c478bd9Sstevel@tonic-gate } 6447c478bd9Sstevel@tonic-gate punt: 645fe54a78eSHai-May Chao timestamp = gethrtime(); 646fe54a78eSHai-May Chao 647fe54a78eSHai-May Chao src = (uint8_t *)×tamp; 648fe54a78eSHai-May Chao dst = (uint8_t *)rmp->rm_mag.rm_seed; 649fe54a78eSHai-May Chao 650fe54a78eSHai-May Chao for (i = 0; i < HASHSIZE; i++) { 651fe54a78eSHai-May Chao dst[i] ^= src[i % sizeof (timestamp)]; 652fe54a78eSHai-May Chao } 653fe54a78eSHai-May Chao 654fe54a78eSHai-May Chao bcopy(rmp->rm_mag.rm_seed, seed, HASHSIZE); 655fe54a78eSHai-May Chao 656fe54a78eSHai-May Chao fips_random_inner(rmp->rm_mag.rm_key, tempout, 657fe54a78eSHai-May Chao seed); 658fe54a78eSHai-May Chao 6597c478bd9Sstevel@tonic-gate if (bytes >= HASHSIZE) { 6607c478bd9Sstevel@tonic-gate size = HASHSIZE; 6617c478bd9Sstevel@tonic-gate } else { 6627c478bd9Sstevel@tonic-gate size = min(bytes, HASHSIZE); 6637c478bd9Sstevel@tonic-gate } 664fe54a78eSHai-May Chao 665fe54a78eSHai-May Chao /* 666fe54a78eSHai-May Chao * FIPS 140-2: Continuous RNG test - each generation 667fe54a78eSHai-May Chao * of an n-bit block shall be compared with the previously 668fe54a78eSHai-May Chao * generated block. Test shall fail if any two compared 669fe54a78eSHai-May Chao * n-bit blocks are equal. 670fe54a78eSHai-May Chao */ 67156498af3SHai-May Chao for (i = 0; i < HASHSIZE/BYTES_IN_WORD; i++) { 672fe54a78eSHai-May Chao if (tempout[i] != rmp->rm_mag.rm_previous[i]) 673fe54a78eSHai-May Chao break; 674fe54a78eSHai-May Chao } 675b5a2d845SHai-May Chao if (i == HASHSIZE/BYTES_IN_WORD) { 676fe54a78eSHai-May Chao cmn_err(CE_WARN, "kcf_random: The value of 160-bit " 677fe54a78eSHai-May Chao "block random bytes are same as the previous " 678fe54a78eSHai-May Chao "one.\n"); 679b5a2d845SHai-May Chao /* discard random bytes and return error */ 680b5a2d845SHai-May Chao return (EIO); 681b5a2d845SHai-May Chao } 682fe54a78eSHai-May Chao 683fe54a78eSHai-May Chao bcopy(tempout, rmp->rm_mag.rm_previous, 684fe54a78eSHai-May Chao HASHSIZE); 685fe54a78eSHai-May Chao 686fe54a78eSHai-May Chao bcopy(tempout, ptr, size); 6877c478bd9Sstevel@tonic-gate ptr += size; 6887c478bd9Sstevel@tonic-gate bytes -= size; 6897c478bd9Sstevel@tonic-gate oblocks++; 6907c478bd9Sstevel@tonic-gate nblock--; 6917c478bd9Sstevel@tonic-gate } while (bytes > 0); 6927c478bd9Sstevel@tonic-gate 693fe54a78eSHai-May Chao /* Zero out sensitive information */ 694fe54a78eSHai-May Chao bzero(seed, HASHSIZE); 695fe54a78eSHai-May Chao bzero(tempout, HASHSIZE); 696fe54a78eSHai-May Chao mutex_exit(&rmp->rm_mag.rm_lock); 6977c478bd9Sstevel@tonic-gate return (0); 6987c478bd9Sstevel@tonic-gate } 6997c478bd9Sstevel@tonic-gate 7007c478bd9Sstevel@tonic-gate /* 7017c478bd9Sstevel@tonic-gate * Per-CPU Random magazines. 7027c478bd9Sstevel@tonic-gate */ 703fe54a78eSHai-May Chao static rndmag_pad_t *rndmag; 7047c478bd9Sstevel@tonic-gate static uint8_t *rndbuf; 7057c478bd9Sstevel@tonic-gate static size_t rndmag_total; 7067c478bd9Sstevel@tonic-gate /* 7077c478bd9Sstevel@tonic-gate * common/os/cpu.c says that platform support code can shrinkwrap 7087c478bd9Sstevel@tonic-gate * max_ncpus. On the off chance that we get loaded very early, we 7097c478bd9Sstevel@tonic-gate * read it exactly once, to copy it here. 7107c478bd9Sstevel@tonic-gate */ 7117c478bd9Sstevel@tonic-gate static uint32_t random_max_ncpus = 0; 7127c478bd9Sstevel@tonic-gate 7137c478bd9Sstevel@tonic-gate /* 7147c478bd9Sstevel@tonic-gate * Boot-time tunables, for experimentation. 7157c478bd9Sstevel@tonic-gate */ 71602f574f0Skais size_t rndmag_threshold = 2560; 71702f574f0Skais size_t rndbuf_len = 5120; 718fa626f0cSkrishna size_t rndmag_size = 1280; 7197c478bd9Sstevel@tonic-gate 7207c478bd9Sstevel@tonic-gate 7217c478bd9Sstevel@tonic-gate int 7227c478bd9Sstevel@tonic-gate kcf_rnd_get_pseudo_bytes(uint8_t *ptr, size_t len) 7237c478bd9Sstevel@tonic-gate { 724fe54a78eSHai-May Chao rndmag_pad_t *rmp; 7257c478bd9Sstevel@tonic-gate uint8_t *cptr, *eptr; 7267c478bd9Sstevel@tonic-gate 7277c478bd9Sstevel@tonic-gate /* 7287c478bd9Sstevel@tonic-gate * Anyone who asks for zero bytes of randomness should get slapped. 7297c478bd9Sstevel@tonic-gate */ 7307c478bd9Sstevel@tonic-gate ASSERT(len > 0); 7317c478bd9Sstevel@tonic-gate 7327c478bd9Sstevel@tonic-gate /* 7337c478bd9Sstevel@tonic-gate * Fast path. 7347c478bd9Sstevel@tonic-gate */ 7357c478bd9Sstevel@tonic-gate for (;;) { 7367c478bd9Sstevel@tonic-gate rmp = &rndmag[CPU->cpu_seqid]; 737fe54a78eSHai-May Chao mutex_enter(&rmp->rm_mag.rm_lock); 7387c478bd9Sstevel@tonic-gate 7397c478bd9Sstevel@tonic-gate /* 7407c478bd9Sstevel@tonic-gate * Big requests bypass buffer and tail-call the 7417c478bd9Sstevel@tonic-gate * generate routine directly. 7427c478bd9Sstevel@tonic-gate */ 7437c478bd9Sstevel@tonic-gate if (len > rndmag_threshold) { 7447c478bd9Sstevel@tonic-gate BUMP_CPU_RND_STATS(rmp, rs_urndOut, len); 7457c478bd9Sstevel@tonic-gate return (rnd_generate_pseudo_bytes(rmp, ptr, len)); 7467c478bd9Sstevel@tonic-gate } 7477c478bd9Sstevel@tonic-gate 748fe54a78eSHai-May Chao cptr = rmp->rm_mag.rm_rptr; 7497c478bd9Sstevel@tonic-gate eptr = cptr + len; 7507c478bd9Sstevel@tonic-gate 751fe54a78eSHai-May Chao if (eptr <= rmp->rm_mag.rm_eptr) { 752fe54a78eSHai-May Chao rmp->rm_mag.rm_rptr = eptr; 7537c478bd9Sstevel@tonic-gate bcopy(cptr, ptr, len); 7547c478bd9Sstevel@tonic-gate BUMP_CPU_RND_STATS(rmp, rs_urndOut, len); 755fe54a78eSHai-May Chao mutex_exit(&rmp->rm_mag.rm_lock); 7567c478bd9Sstevel@tonic-gate 7577c478bd9Sstevel@tonic-gate return (0); 7587c478bd9Sstevel@tonic-gate } 7597c478bd9Sstevel@tonic-gate /* 7607c478bd9Sstevel@tonic-gate * End fast path. 7617c478bd9Sstevel@tonic-gate */ 762fe54a78eSHai-May Chao rmp->rm_mag.rm_rptr = rmp->rm_mag.rm_buffer; 7637c478bd9Sstevel@tonic-gate /* 7647c478bd9Sstevel@tonic-gate * Note: We assume the generate routine always succeeds 7657c478bd9Sstevel@tonic-gate * in this case (because it does at present..) 7667c478bd9Sstevel@tonic-gate * It also always releases rm_lock. 7677c478bd9Sstevel@tonic-gate */ 768fe54a78eSHai-May Chao (void) rnd_generate_pseudo_bytes(rmp, rmp->rm_mag.rm_buffer, 7697c478bd9Sstevel@tonic-gate rndbuf_len); 7707c478bd9Sstevel@tonic-gate } 7717c478bd9Sstevel@tonic-gate } 7727c478bd9Sstevel@tonic-gate 7737c478bd9Sstevel@tonic-gate /* 7747c478bd9Sstevel@tonic-gate * We set up (empty) magazines for all of max_ncpus, possibly wasting a 7757c478bd9Sstevel@tonic-gate * little memory on big systems that don't have the full set installed. 7767c478bd9Sstevel@tonic-gate * See above; "empty" means "rptr equal to eptr"; this will trigger the 7777c478bd9Sstevel@tonic-gate * refill path in rnd_get_pseudo_bytes above on the first call for each CPU. 7787c478bd9Sstevel@tonic-gate * 7797c478bd9Sstevel@tonic-gate * TODO: make rndmag_size tunable at run time! 7807c478bd9Sstevel@tonic-gate */ 7817c478bd9Sstevel@tonic-gate static void 7827c478bd9Sstevel@tonic-gate rnd_alloc_magazines() 7837c478bd9Sstevel@tonic-gate { 784fe54a78eSHai-May Chao rndmag_pad_t *rmp; 7857c478bd9Sstevel@tonic-gate int i; 786fe54a78eSHai-May Chao uint8_t discard_buf[HASHSIZE]; 7877c478bd9Sstevel@tonic-gate 7887c478bd9Sstevel@tonic-gate rndbuf_len = roundup(rndbuf_len, HASHSIZE); 7897c478bd9Sstevel@tonic-gate if (rndmag_size < rndbuf_len) 7907c478bd9Sstevel@tonic-gate rndmag_size = rndbuf_len; 7917c478bd9Sstevel@tonic-gate rndmag_size = roundup(rndmag_size, RND_CPU_CACHE_SIZE); 7927c478bd9Sstevel@tonic-gate 7937c478bd9Sstevel@tonic-gate random_max_ncpus = max_ncpus; 7947c478bd9Sstevel@tonic-gate rndmag_total = rndmag_size * random_max_ncpus; 7957c478bd9Sstevel@tonic-gate 7967c478bd9Sstevel@tonic-gate rndbuf = kmem_alloc(rndmag_total, KM_SLEEP); 797fe54a78eSHai-May Chao rndmag = kmem_zalloc(sizeof (rndmag_pad_t) * random_max_ncpus, 798fe54a78eSHai-May Chao KM_SLEEP); 7997c478bd9Sstevel@tonic-gate 8007c478bd9Sstevel@tonic-gate for (i = 0; i < random_max_ncpus; i++) { 8017c478bd9Sstevel@tonic-gate uint8_t *buf; 8027c478bd9Sstevel@tonic-gate 8037c478bd9Sstevel@tonic-gate rmp = &rndmag[i]; 804fe54a78eSHai-May Chao mutex_init(&rmp->rm_mag.rm_lock, NULL, MUTEX_DRIVER, NULL); 8057c478bd9Sstevel@tonic-gate 8067c478bd9Sstevel@tonic-gate buf = rndbuf + i * rndmag_size; 8077c478bd9Sstevel@tonic-gate 808fe54a78eSHai-May Chao rmp->rm_mag.rm_buffer = buf; 809fe54a78eSHai-May Chao rmp->rm_mag.rm_eptr = buf + rndbuf_len; 810fe54a78eSHai-May Chao rmp->rm_mag.rm_rptr = buf + rndbuf_len; 811fe54a78eSHai-May Chao rmp->rm_mag.rm_oblocks = 1; 812fe54a78eSHai-May Chao 813fe54a78eSHai-May Chao mutex_enter(&rndpool_lock); 814fe54a78eSHai-May Chao /* 815fe54a78eSHai-May Chao * FIPS 140-2: the first n-bit (n > 15) block generated 816fe54a78eSHai-May Chao * after power-up, initialization, or reset shall not 817fe54a78eSHai-May Chao * be used, but shall be saved for comparison. 818fe54a78eSHai-May Chao */ 819fe54a78eSHai-May Chao (void) rnd_get_bytes(discard_buf, 8208b502715SKrishna Yenduri HMAC_KEYSIZE, ALWAYS_EXTRACT); 821fe54a78eSHai-May Chao bcopy(discard_buf, rmp->rm_mag.rm_previous, 822fe54a78eSHai-May Chao HMAC_KEYSIZE); 823fe54a78eSHai-May Chao /* rnd_get_bytes() will call mutex_exit(&rndpool_lock) */ 824fe54a78eSHai-May Chao mutex_enter(&rndpool_lock); 825fe54a78eSHai-May Chao (void) rnd_get_bytes((uint8_t *)rmp->rm_mag.rm_key, 8268b502715SKrishna Yenduri HMAC_KEYSIZE, ALWAYS_EXTRACT); 827fe54a78eSHai-May Chao /* rnd_get_bytes() will call mutex_exit(&rndpool_lock) */ 828fe54a78eSHai-May Chao mutex_enter(&rndpool_lock); 829fe54a78eSHai-May Chao (void) rnd_get_bytes((uint8_t *)rmp->rm_mag.rm_seed, 8308b502715SKrishna Yenduri HMAC_KEYSIZE, ALWAYS_EXTRACT); 8317c478bd9Sstevel@tonic-gate } 8327c478bd9Sstevel@tonic-gate } 8337c478bd9Sstevel@tonic-gate 8347c478bd9Sstevel@tonic-gate void 8357c478bd9Sstevel@tonic-gate kcf_rnd_schedule_timeout(boolean_t do_mech2id) 8367c478bd9Sstevel@tonic-gate { 8377c478bd9Sstevel@tonic-gate clock_t ut; /* time in microseconds */ 8387c478bd9Sstevel@tonic-gate 8397c478bd9Sstevel@tonic-gate if (do_mech2id) 8407c478bd9Sstevel@tonic-gate rngmech_type = crypto_mech2id(SUN_RANDOM); 8417c478bd9Sstevel@tonic-gate 8427c478bd9Sstevel@tonic-gate /* 8437c478bd9Sstevel@tonic-gate * The new timeout value is taken from the buffer of random bytes. 8447c478bd9Sstevel@tonic-gate * We're merely reading the first 32 bits from the buffer here, not 8457c478bd9Sstevel@tonic-gate * consuming any random bytes. 8467c478bd9Sstevel@tonic-gate * The timeout multiplier value is a random value between 0.5 sec and 8477c478bd9Sstevel@tonic-gate * 1.544480 sec (0.5 sec + 0xFF000 microseconds). 8487c478bd9Sstevel@tonic-gate * The new timeout is TIMEOUT_INTERVAL times that multiplier. 8497c478bd9Sstevel@tonic-gate */ 8507c478bd9Sstevel@tonic-gate ut = 500000 + (clock_t)((((uint32_t)rndpool[findex]) << 12) & 0xFF000); 8517c478bd9Sstevel@tonic-gate kcf_rndtimeout_id = timeout(rnd_handler, NULL, 8527c478bd9Sstevel@tonic-gate TIMEOUT_INTERVAL * drv_usectohz(ut)); 8537c478bd9Sstevel@tonic-gate } 8547c478bd9Sstevel@tonic-gate 8557c478bd9Sstevel@tonic-gate /* 8569d31afc5SKrishna Yenduri * Called from the driver for a poll on /dev/random 8579d31afc5SKrishna Yenduri * . POLLOUT always succeeds. 8589d31afc5SKrishna Yenduri * . POLLIN and POLLRDNORM will block until a 8599d31afc5SKrishna Yenduri * minimum amount of entropy is available. 8609d31afc5SKrishna Yenduri * 8617c478bd9Sstevel@tonic-gate * &rnd_pollhead is passed in *phpp in order to indicate the calling thread 8627c478bd9Sstevel@tonic-gate * will block. When enough random bytes are available, later, the timeout 8637c478bd9Sstevel@tonic-gate * handler routine will issue the pollwakeup() calls. 8647c478bd9Sstevel@tonic-gate */ 8657c478bd9Sstevel@tonic-gate void 8669d31afc5SKrishna Yenduri kcf_rnd_chpoll(short events, int anyyet, short *reventsp, 8679d31afc5SKrishna Yenduri struct pollhead **phpp) 8687c478bd9Sstevel@tonic-gate { 8699d31afc5SKrishna Yenduri *reventsp = events & POLLOUT; 8709d31afc5SKrishna Yenduri 8719d31afc5SKrishna Yenduri if (events & (POLLIN | POLLRDNORM)) { 8729d31afc5SKrishna Yenduri /* 8739d31afc5SKrishna Yenduri * Sampling of rnbyte_cnt is an atomic 8749d31afc5SKrishna Yenduri * operation. Hence we do not need any locking. 8759d31afc5SKrishna Yenduri */ 8769d31afc5SKrishna Yenduri if (rnbyte_cnt >= MINEXTRACTBYTES) 8779d31afc5SKrishna Yenduri *reventsp |= (events & (POLLIN | POLLRDNORM)); 8787c478bd9Sstevel@tonic-gate } 8799d31afc5SKrishna Yenduri 8809d31afc5SKrishna Yenduri if (*reventsp == 0 && !anyyet) 8819d31afc5SKrishna Yenduri *phpp = &rnd_pollhead; 8827c478bd9Sstevel@tonic-gate } 8837c478bd9Sstevel@tonic-gate 8847c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 8857c478bd9Sstevel@tonic-gate static void 8867c478bd9Sstevel@tonic-gate rnd_handler(void *arg) 8877c478bd9Sstevel@tonic-gate { 8887c478bd9Sstevel@tonic-gate int len = 0; 8897c478bd9Sstevel@tonic-gate 890f317a3a3Skrishna if (!rng_prov_found && rng_ok_to_log) { 891f317a3a3Skrishna cmn_err(CE_WARN, "No randomness provider enabled for " 892f317a3a3Skrishna "/dev/random. Use cryptoadm(1M) to enable a provider."); 893f317a3a3Skrishna rng_ok_to_log = B_FALSE; 894f317a3a3Skrishna } 895f317a3a3Skrishna 8967c478bd9Sstevel@tonic-gate if (num_waiters > 0) 897f5229b7eSKrishna Yenduri /* 898f5229b7eSKrishna Yenduri * Note: len has no relationship with how many bytes 899f5229b7eSKrishna Yenduri * a poll thread needs. 900f5229b7eSKrishna Yenduri */ 9017c478bd9Sstevel@tonic-gate len = MAXEXTRACTBYTES; 9027c478bd9Sstevel@tonic-gate else if (rnbyte_cnt < RNDPOOLSIZE) 9037c478bd9Sstevel@tonic-gate len = MINEXTRACTBYTES; 9047c478bd9Sstevel@tonic-gate 905f5229b7eSKrishna Yenduri /* 906f5229b7eSKrishna Yenduri * Only one thread gets to set rngprov_task_idle at a given point 907f5229b7eSKrishna Yenduri * of time and the order of the writes is defined. Also, it is OK 908f5229b7eSKrishna Yenduri * if we read an older value of it and skip the dispatch once 909f5229b7eSKrishna Yenduri * since we will get the correct value during the next time here. 910f5229b7eSKrishna Yenduri * So, no locking is needed here. 911f5229b7eSKrishna Yenduri */ 912f5229b7eSKrishna Yenduri if (len > 0 && rngprov_task_idle) { 913f5229b7eSKrishna Yenduri rngprov_task_idle = B_FALSE; 914f5229b7eSKrishna Yenduri 915f5229b7eSKrishna Yenduri /* 916f5229b7eSKrishna Yenduri * It is OK if taskq_dispatch fails here. We will retry 917f5229b7eSKrishna Yenduri * the next time around. Meanwhile, a thread doing a 918f5229b7eSKrishna Yenduri * read() will go to the provider directly, if the 919f5229b7eSKrishna Yenduri * cache becomes empty. 920f5229b7eSKrishna Yenduri */ 921f5229b7eSKrishna Yenduri if (taskq_dispatch(system_taskq, rngprov_task, 922f5229b7eSKrishna Yenduri (void *)(uintptr_t)len, TQ_NOSLEEP | TQ_NOQUEUE) == 0) { 923f5229b7eSKrishna Yenduri rngprov_task_idle = B_TRUE; 924f5229b7eSKrishna Yenduri } 9257c478bd9Sstevel@tonic-gate } 9267c478bd9Sstevel@tonic-gate 9277c478bd9Sstevel@tonic-gate mutex_enter(&rndpool_lock); 9287c478bd9Sstevel@tonic-gate /* 9297c478bd9Sstevel@tonic-gate * Wake up threads waiting in poll() or for enough accumulated 9307c478bd9Sstevel@tonic-gate * random bytes to read from /dev/random. In case a poll() is 9317c478bd9Sstevel@tonic-gate * concurrent with a read(), the polling process may be woken up 9327c478bd9Sstevel@tonic-gate * indicating that enough randomness is now available for reading, 9337c478bd9Sstevel@tonic-gate * and another process *steals* the bits from the pool, causing the 9347c478bd9Sstevel@tonic-gate * subsequent read() from the first process to block. It is acceptable 9357c478bd9Sstevel@tonic-gate * since the blocking will eventually end, after the timeout 9367c478bd9Sstevel@tonic-gate * has expired enough times to honor the read. 9377c478bd9Sstevel@tonic-gate * 9387c478bd9Sstevel@tonic-gate * Note - Since we hold the rndpool_lock across the pollwakeup() call 9397c478bd9Sstevel@tonic-gate * we MUST NOT grab the rndpool_lock in kcf_rndchpoll(). 9407c478bd9Sstevel@tonic-gate */ 9417c478bd9Sstevel@tonic-gate if (rnbyte_cnt >= MINEXTRACTBYTES) 9427c478bd9Sstevel@tonic-gate pollwakeup(&rnd_pollhead, POLLIN | POLLRDNORM); 9437c478bd9Sstevel@tonic-gate 9447c478bd9Sstevel@tonic-gate if (num_waiters > 0) 9457c478bd9Sstevel@tonic-gate cv_broadcast(&rndpool_read_cv); 9467c478bd9Sstevel@tonic-gate mutex_exit(&rndpool_lock); 9477c478bd9Sstevel@tonic-gate 9487c478bd9Sstevel@tonic-gate kcf_rnd_schedule_timeout(B_FALSE); 9497c478bd9Sstevel@tonic-gate } 9507c478bd9Sstevel@tonic-gate 9517c478bd9Sstevel@tonic-gate static void 9527c478bd9Sstevel@tonic-gate rndc_addbytes(uint8_t *ptr, size_t len) 9537c478bd9Sstevel@tonic-gate { 9547c478bd9Sstevel@tonic-gate ASSERT(ptr != NULL && len > 0); 9557c478bd9Sstevel@tonic-gate ASSERT(rnbyte_cnt <= RNDPOOLSIZE); 9567c478bd9Sstevel@tonic-gate 9577c478bd9Sstevel@tonic-gate mutex_enter(&rndpool_lock); 9587c478bd9Sstevel@tonic-gate while ((len > 0) && (rnbyte_cnt < RNDPOOLSIZE)) { 9597c478bd9Sstevel@tonic-gate rndpool[rindex] ^= *ptr; 9607c478bd9Sstevel@tonic-gate ptr++; len--; 9617c478bd9Sstevel@tonic-gate rindex = (rindex + 1) & (RNDPOOLSIZE - 1); 9627c478bd9Sstevel@tonic-gate rnbyte_cnt++; 9637c478bd9Sstevel@tonic-gate } 9647c478bd9Sstevel@tonic-gate 9657c478bd9Sstevel@tonic-gate /* Handle buffer full case */ 9667c478bd9Sstevel@tonic-gate while (len > 0) { 9677c478bd9Sstevel@tonic-gate rndpool[rindex] ^= *ptr; 9687c478bd9Sstevel@tonic-gate ptr++; len--; 9697c478bd9Sstevel@tonic-gate findex = rindex = (rindex + 1) & (RNDPOOLSIZE - 1); 9707c478bd9Sstevel@tonic-gate } 9717c478bd9Sstevel@tonic-gate mutex_exit(&rndpool_lock); 9727c478bd9Sstevel@tonic-gate } 9737c478bd9Sstevel@tonic-gate 9747c478bd9Sstevel@tonic-gate /* 9757c478bd9Sstevel@tonic-gate * Caller should check len <= rnbyte_cnt under the 9767c478bd9Sstevel@tonic-gate * rndpool_lock before calling. 9777c478bd9Sstevel@tonic-gate */ 9787c478bd9Sstevel@tonic-gate static void 9797c478bd9Sstevel@tonic-gate rndc_getbytes(uint8_t *ptr, size_t len) 9807c478bd9Sstevel@tonic-gate { 9817c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&rndpool_lock)); 9827c478bd9Sstevel@tonic-gate ASSERT(len <= rnbyte_cnt && rnbyte_cnt <= RNDPOOLSIZE); 9837c478bd9Sstevel@tonic-gate 9847c478bd9Sstevel@tonic-gate BUMP_RND_STATS(rs_rndcOut, len); 9857c478bd9Sstevel@tonic-gate 9867c478bd9Sstevel@tonic-gate while (len > 0) { 9877c478bd9Sstevel@tonic-gate *ptr = rndpool[findex]; 9887c478bd9Sstevel@tonic-gate ptr++; len--; 9897c478bd9Sstevel@tonic-gate findex = (findex + 1) & (RNDPOOLSIZE - 1); 9907c478bd9Sstevel@tonic-gate rnbyte_cnt--; 9917c478bd9Sstevel@tonic-gate } 9927c478bd9Sstevel@tonic-gate } 9937c478bd9Sstevel@tonic-gate 9947c478bd9Sstevel@tonic-gate /* Random number exported entry points */ 9957c478bd9Sstevel@tonic-gate 9967c478bd9Sstevel@tonic-gate /* 9977c478bd9Sstevel@tonic-gate * Mix the supplied bytes into the entropy pool of a kCF 9987c478bd9Sstevel@tonic-gate * RNG provider. 9997c478bd9Sstevel@tonic-gate */ 10007c478bd9Sstevel@tonic-gate int 10018047c9fbSmcpowers random_add_pseudo_entropy(uint8_t *ptr, size_t len, uint_t entropy_est) 10028047c9fbSmcpowers { 10038047c9fbSmcpowers if (len < 1) 10048047c9fbSmcpowers return (-1); 10058047c9fbSmcpowers 10068047c9fbSmcpowers rngprov_seed(ptr, len, entropy_est, 0); 10078047c9fbSmcpowers 10088047c9fbSmcpowers return (0); 10098047c9fbSmcpowers } 10108047c9fbSmcpowers 10118047c9fbSmcpowers /* 10128047c9fbSmcpowers * Mix the supplied bytes into the entropy pool of a kCF 10138047c9fbSmcpowers * RNG provider. Mix immediately. 10148047c9fbSmcpowers */ 10158047c9fbSmcpowers int 10168047c9fbSmcpowers random_add_entropy(uint8_t *ptr, size_t len, uint_t entropy_est) 10177c478bd9Sstevel@tonic-gate { 10187c478bd9Sstevel@tonic-gate if (len < 1) 10197c478bd9Sstevel@tonic-gate return (-1); 10207c478bd9Sstevel@tonic-gate 10218047c9fbSmcpowers rngprov_seed(ptr, len, entropy_est, CRYPTO_SEED_NOW); 10227c478bd9Sstevel@tonic-gate 10237c478bd9Sstevel@tonic-gate return (0); 10247c478bd9Sstevel@tonic-gate } 10257c478bd9Sstevel@tonic-gate 10267c478bd9Sstevel@tonic-gate /* 10277c478bd9Sstevel@tonic-gate * Get bytes from the /dev/urandom generator. This function 10287c478bd9Sstevel@tonic-gate * always succeeds. Returns 0. 10297c478bd9Sstevel@tonic-gate */ 10307c478bd9Sstevel@tonic-gate int 10317c478bd9Sstevel@tonic-gate random_get_pseudo_bytes(uint8_t *ptr, size_t len) 10327c478bd9Sstevel@tonic-gate { 10337c478bd9Sstevel@tonic-gate ASSERT(!mutex_owned(&rndpool_lock)); 10347c478bd9Sstevel@tonic-gate 10357c478bd9Sstevel@tonic-gate if (len < 1) 10367c478bd9Sstevel@tonic-gate return (0); 10377c478bd9Sstevel@tonic-gate return (kcf_rnd_get_pseudo_bytes(ptr, len)); 10387c478bd9Sstevel@tonic-gate } 10397c478bd9Sstevel@tonic-gate 10407c478bd9Sstevel@tonic-gate /* 10417c478bd9Sstevel@tonic-gate * Get bytes from the /dev/random generator. Returns 0 10427c478bd9Sstevel@tonic-gate * on success. Returns EAGAIN if there is insufficient entropy. 10437c478bd9Sstevel@tonic-gate */ 10447c478bd9Sstevel@tonic-gate int 10457c478bd9Sstevel@tonic-gate random_get_bytes(uint8_t *ptr, size_t len) 10467c478bd9Sstevel@tonic-gate { 10477c478bd9Sstevel@tonic-gate ASSERT(!mutex_owned(&rndpool_lock)); 10487c478bd9Sstevel@tonic-gate 10497c478bd9Sstevel@tonic-gate if (len < 1) 10507c478bd9Sstevel@tonic-gate return (0); 10518b502715SKrishna Yenduri return (kcf_rnd_get_bytes(ptr, len, B_TRUE)); 10527c478bd9Sstevel@tonic-gate } 1053*73556491SAnthony Scarpino 1054*73556491SAnthony Scarpino /* 1055*73556491SAnthony Scarpino * The two functions below are identical to random_get_pseudo_bytes() and 1056*73556491SAnthony Scarpino * random_get_bytes_fips, this function is called for consumers that want 1057*73556491SAnthony Scarpino * FIPS 140-2. This function waits until the FIPS boundary can be verified. 1058*73556491SAnthony Scarpino */ 1059*73556491SAnthony Scarpino 1060*73556491SAnthony Scarpino /* 1061*73556491SAnthony Scarpino * Get bytes from the /dev/urandom generator. This function 1062*73556491SAnthony Scarpino * always succeeds. Returns 0. 1063*73556491SAnthony Scarpino */ 1064*73556491SAnthony Scarpino int 1065*73556491SAnthony Scarpino random_get_pseudo_bytes_fips140(uint8_t *ptr, size_t len) 1066*73556491SAnthony Scarpino { 1067*73556491SAnthony Scarpino ASSERT(!mutex_owned(&rndpool_lock)); 1068*73556491SAnthony Scarpino 1069*73556491SAnthony Scarpino mutex_enter(&fips140_mode_lock); 1070*73556491SAnthony Scarpino while (global_fips140_mode < FIPS140_MODE_ENABLED) { 1071*73556491SAnthony Scarpino cv_wait(&cv_fips140, &fips140_mode_lock); 1072*73556491SAnthony Scarpino } 1073*73556491SAnthony Scarpino mutex_exit(&fips140_mode_lock); 1074*73556491SAnthony Scarpino 1075*73556491SAnthony Scarpino if (len < 1) 1076*73556491SAnthony Scarpino return (0); 1077*73556491SAnthony Scarpino return (kcf_rnd_get_pseudo_bytes(ptr, len)); 1078*73556491SAnthony Scarpino } 1079*73556491SAnthony Scarpino 1080*73556491SAnthony Scarpino /* 1081*73556491SAnthony Scarpino * Get bytes from the /dev/random generator. Returns 0 1082*73556491SAnthony Scarpino * on success. Returns EAGAIN if there is insufficient entropy. 1083*73556491SAnthony Scarpino */ 1084*73556491SAnthony Scarpino int 1085*73556491SAnthony Scarpino random_get_bytes_fips140(uint8_t *ptr, size_t len) 1086*73556491SAnthony Scarpino { 1087*73556491SAnthony Scarpino ASSERT(!mutex_owned(&rndpool_lock)); 1088*73556491SAnthony Scarpino 1089*73556491SAnthony Scarpino mutex_enter(&fips140_mode_lock); 1090*73556491SAnthony Scarpino while (global_fips140_mode < FIPS140_MODE_ENABLED) { 1091*73556491SAnthony Scarpino cv_wait(&cv_fips140, &fips140_mode_lock); 1092*73556491SAnthony Scarpino } 1093*73556491SAnthony Scarpino mutex_exit(&fips140_mode_lock); 1094*73556491SAnthony Scarpino 1095*73556491SAnthony Scarpino if (len < 1) 1096*73556491SAnthony Scarpino return (0); 1097*73556491SAnthony Scarpino return (kcf_rnd_get_bytes(ptr, len, B_TRUE)); 1098*73556491SAnthony Scarpino } 1099