| OLD | NEW |
| (Empty) |
| 1 /* This Source Code Form is subject to the terms of the Mozilla Public | |
| 2 * License, v. 2.0. If a copy of the MPL was not distributed with this | |
| 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
| 4 /* $Id: drbg.c,v 1.12 2012/12/12 19:22:39 wtc%google.com Exp $ */ | |
| 5 | |
| 6 #ifdef FREEBL_NO_DEPEND | |
| 7 #include "stubs.h" | |
| 8 #endif | |
| 9 | |
| 10 #include "prerror.h" | |
| 11 #include "secerr.h" | |
| 12 | |
| 13 #include "prtypes.h" | |
| 14 #include "prinit.h" | |
| 15 #include "blapi.h" | |
| 16 #include "blapii.h" | |
| 17 #include "nssilock.h" | |
| 18 #include "secitem.h" | |
| 19 #include "sha_fast.h" | |
| 20 #include "sha256.h" | |
| 21 #include "secrng.h" /* for RNG_SystemRNG() */ | |
| 22 #include "secmpi.h" | |
| 23 | |
| 24 /* PRNG_SEEDLEN defined in NIST SP 800-90 section 10.1 | |
| 25 * for SHA-1, SHA-224, and SHA-256 it's 440 bits. | |
| 26 * for SHA-384 and SHA-512 it's 888 bits */ | |
| 27 #define PRNG_SEEDLEN (440/PR_BITS_PER_BYTE) | |
| 28 static const PRInt64 PRNG_MAX_ADDITIONAL_BYTES = LL_INIT(0x1, 0x0); | |
| 29 /* 2^35 bits or 2^32 bytes */ | |
| 30 #define PRNG_MAX_REQUEST_SIZE 0x10000 /* 2^19 bits or 2^16 bytes */ | |
| 31 #define PRNG_ADDITONAL_DATA_CACHE_SIZE (8*1024) /* must be less than | |
| 32 * PRNG_MAX_ADDITIONAL_BYTES | |
| 33 */ | |
| 34 | |
| 35 /* RESEED_COUNT is how many calls to the prng before we need to reseed | |
| 36 * under normal NIST rules, you must return an error. In the NSS case, we | |
| 37 * self-reseed with RNG_SystemRNG(). Count can be a large number. For code | |
| 38 * simplicity, we specify count with 2 components: RESEED_BYTE (which is | |
| 39 * the same as LOG256(RESEED_COUNT)) and RESEED_VALUE (which is the same as | |
| 40 * RESEED_COUNT / (256 ^ RESEED_BYTE)). Another way to look at this is | |
| 41 * RESEED_COUNT = RESEED_VALUE * (256 ^ RESEED_BYTE). For Hash based DRBG | |
| 42 * we use the maximum count value, 2^48, or RESEED_BYTE=6 and RESEED_VALUE=1 | |
| 43 */ | |
| 44 #define RESEED_BYTE 6 | |
| 45 #define RESEED_VALUE 1 | |
| 46 | |
| 47 #define PRNG_RESET_RESEED_COUNT(rng) \ | |
| 48 PORT_Memset((rng)->reseed_counter, 0, sizeof (rng)->reseed_counter); \ | |
| 49 (rng)->reseed_counter[RESEED_BYTE] = 1; | |
| 50 | |
| 51 | |
| 52 /* | |
| 53 * The actual values of this enum are specified in SP 800-90, 10.1.1.* | |
| 54 * The spec does not name the types, it only uses bare values | |
| 55 */ | |
| 56 typedef enum { | |
| 57 prngCGenerateType = 0, /* used when creating a new 'C' */ | |
| 58 prngReseedType = 1, /* used in reseeding */ | |
| 59 prngAdditionalDataType = 2, /* used in mixing additional data */ | |
| 60 prngGenerateByteType = 3 /* used when mixing internal state while | |
| 61 * generating bytes */ | |
| 62 } prngVTypes; | |
| 63 | |
| 64 /* | |
| 65 * Global RNG context | |
| 66 */ | |
| 67 struct RNGContextStr { | |
| 68 PZLock *lock; /* Lock to serialize access to global rng */ | |
| 69 /* | |
| 70 * NOTE, a number of steps in the drbg algorithm need to hash | |
| 71 * V_type || V. The code, therefore, depends on the V array following | |
| 72 * immediately after V_type to avoid extra copies. To accomplish this | |
| 73 * in a way that compiliers can't perturb, we declare V_type and V | |
| 74 * as a V_Data array and reference them by macros */ | |
| 75 PRUint8 V_Data[PRNG_SEEDLEN+1]; /* internal state variables */ | |
| 76 #define V_type V_Data[0] | |
| 77 #define V(rng) (((rng)->V_Data)+1) | |
| 78 #define VSize(rng) ((sizeof (rng)->V_Data) -1) | |
| 79 PRUint8 C[PRNG_SEEDLEN]; /* internal state variables */ | |
| 80 PRUint8 oldV[PRNG_SEEDLEN]; /* for continuous rng checking */ | |
| 81 /* If we get calls for the PRNG to return less than the length of our | |
| 82 * hash, we extend the request for a full hash (since we'll be doing | |
| 83 * the full hash anyway). Future requests for random numbers are fulfilled | |
| 84 * from the remainder of the bytes we generated. Requests for bytes longer | |
| 85 * than the hash size are fulfilled directly from the HashGen function | |
| 86 * of the random number generator. */ | |
| 87 PRUint8 reseed_counter[RESEED_BYTE+1]; /* number of requests since the | |
| 88 * last reseed. Need only be | |
| 89 * big enough to hold the whole | |
| 90 * reseed count */ | |
| 91 PRUint8 data[SHA256_LENGTH]; /* when we request less than a block | |
| 92 * save the rest of the rng output for | |
| 93 * another partial block */ | |
| 94 PRUint8 dataAvail; /* # bytes of output available in our cache, | |
| 95 * [0...SHA256_LENGTH] */ | |
| 96 /* store additional data that has been shovelled off to us by | |
| 97 * RNG_RandomUpdate. */ | |
| 98 PRUint8 additionalDataCache[PRNG_ADDITONAL_DATA_CACHE_SIZE]; | |
| 99 PRUint32 additionalAvail; | |
| 100 PRBool isValid; /* false if RNG reaches an invalid state */ | |
| 101 }; | |
| 102 | |
| 103 typedef struct RNGContextStr RNGContext; | |
| 104 static RNGContext *globalrng = NULL; | |
| 105 static RNGContext theGlobalRng; | |
| 106 | |
| 107 | |
| 108 /* | |
| 109 * The next several functions are derived from the NIST SP 800-90 | |
| 110 * spec. In these functions, an attempt was made to use names consistent | |
| 111 * with the names in the spec, even if they differ from normal NSS usage. | |
| 112 */ | |
| 113 | |
| 114 /* | |
| 115 * Hash Derive function defined in NISP SP 800-90 Section 10.4.1. | |
| 116 * This function is used in the Instantiate and Reseed functions. | |
| 117 * | |
| 118 * NOTE: requested_bytes cannot overlap with input_string_1 or input_string_2. | |
| 119 * input_string_1 and input_string_2 are logically concatentated. | |
| 120 * input_string_1 must be supplied. | |
| 121 * if input_string_2 is not supplied, NULL should be passed for this parameter. | |
| 122 */ | |
| 123 static SECStatus | |
| 124 prng_Hash_df(PRUint8 *requested_bytes, unsigned int no_of_bytes_to_return, | |
| 125 const PRUint8 *input_string_1, unsigned int input_string_1_len, | |
| 126 const PRUint8 *input_string_2, unsigned int input_string_2_len) | |
| 127 { | |
| 128 SHA256Context ctx; | |
| 129 PRUint32 tmp; | |
| 130 PRUint8 counter; | |
| 131 | |
| 132 tmp=SHA_HTONL(no_of_bytes_to_return*8); | |
| 133 | |
| 134 for (counter = 1 ; no_of_bytes_to_return > 0; counter++) { | |
| 135 unsigned int hash_return_len; | |
| 136 SHA256_Begin(&ctx); | |
| 137 SHA256_Update(&ctx, &counter, 1); | |
| 138 SHA256_Update(&ctx, (unsigned char *)&tmp, sizeof tmp); | |
| 139 SHA256_Update(&ctx, input_string_1, input_string_1_len); | |
| 140 if (input_string_2) { | |
| 141 SHA256_Update(&ctx, input_string_2, input_string_2_len); | |
| 142 } | |
| 143 SHA256_End(&ctx, requested_bytes, &hash_return_len, | |
| 144 no_of_bytes_to_return); | |
| 145 requested_bytes += hash_return_len; | |
| 146 no_of_bytes_to_return -= hash_return_len; | |
| 147 } | |
| 148 return SECSuccess; | |
| 149 } | |
| 150 | |
| 151 | |
| 152 /* | |
| 153 * Hash_DRBG Instantiate NIST SP 800-80 10.1.1.2 | |
| 154 * | |
| 155 * NOTE: bytes & len are entropy || nonce || personalization_string. In | |
| 156 * normal operation, NSS calculates them all together in a single call. | |
| 157 */ | |
| 158 static SECStatus | |
| 159 prng_instantiate(RNGContext *rng, const PRUint8 *bytes, unsigned int len) | |
| 160 { | |
| 161 if (len < PRNG_SEEDLEN) { | |
| 162 /* if the seedlen is to small, it's probably because we failed to get | |
| 163 * enough random data */ | |
| 164 PORT_SetError(SEC_ERROR_NEED_RANDOM); | |
| 165 return SECFailure; | |
| 166 } | |
| 167 prng_Hash_df(V(rng), VSize(rng), bytes, len, NULL, 0); | |
| 168 rng->V_type = prngCGenerateType; | |
| 169 prng_Hash_df(rng->C,sizeof rng->C,rng->V_Data,sizeof rng->V_Data,NULL,0); | |
| 170 PRNG_RESET_RESEED_COUNT(rng) | |
| 171 return SECSuccess; | |
| 172 } | |
| 173 | |
| 174 | |
| 175 /* | |
| 176 * Update the global random number generator with more seeding | |
| 177 * material. Use the Hash_DRBG reseed algorithm from NIST SP-800-90 | |
| 178 * section 10.1.1.3 | |
| 179 * | |
| 180 * If entropy is NULL, it is fetched from the noise generator. | |
| 181 */ | |
| 182 static SECStatus | |
| 183 prng_reseed(RNGContext *rng, const PRUint8 *entropy, unsigned int entropy_len, | |
| 184 const PRUint8 *additional_input, unsigned int additional_input_len) | |
| 185 { | |
| 186 PRUint8 noiseData[(sizeof rng->V_Data)+PRNG_SEEDLEN]; | |
| 187 PRUint8 *noise = &noiseData[0]; | |
| 188 | |
| 189 /* if entropy wasn't supplied, fetch it. (normal operation case) */ | |
| 190 if (entropy == NULL) { | |
| 191 entropy_len = (unsigned int) RNG_SystemRNG( | |
| 192 &noiseData[sizeof rng->V_Data], PRNG_SEEDLEN); | |
| 193 } else { | |
| 194 /* NOTE: this code is only available for testing, not to applications */ | |
| 195 /* if entropy was too big for the stack variable, get it from malloc */ | |
| 196 if (entropy_len > PRNG_SEEDLEN) { | |
| 197 noise = PORT_Alloc(entropy_len + (sizeof rng->V_Data)); | |
| 198 if (noise == NULL) { | |
| 199 return SECFailure; | |
| 200 } | |
| 201 } | |
| 202 PORT_Memcpy(&noise[sizeof rng->V_Data],entropy, entropy_len); | |
| 203 } | |
| 204 | |
| 205 if (entropy_len < 256/PR_BITS_PER_BYTE) { | |
| 206 /* noise == &noiseData[0] at this point, so nothing to free */ | |
| 207 PORT_SetError(SEC_ERROR_NEED_RANDOM); | |
| 208 return SECFailure; | |
| 209 } | |
| 210 | |
| 211 rng->V_type = prngReseedType; | |
| 212 PORT_Memcpy(noise, rng->V_Data, sizeof rng->V_Data); | |
| 213 prng_Hash_df(V(rng), VSize(rng), noise, (sizeof rng->V_Data) + entropy_len, | |
| 214 additional_input, additional_input_len); | |
| 215 /* clear potential CSP */ | |
| 216 PORT_Memset(noise, 0, (sizeof rng->V_Data) + entropy_len); | |
| 217 rng->V_type = prngCGenerateType; | |
| 218 prng_Hash_df(rng->C,sizeof rng->C,rng->V_Data,sizeof rng->V_Data,NULL,0); | |
| 219 PRNG_RESET_RESEED_COUNT(rng) | |
| 220 | |
| 221 if (noise != &noiseData[0]) { | |
| 222 PORT_Free(noise); | |
| 223 } | |
| 224 return SECSuccess; | |
| 225 } | |
| 226 | |
| 227 /* | |
| 228 * SP 800-90 requires we rerun our health tests on reseed | |
| 229 */ | |
| 230 static SECStatus | |
| 231 prng_reseed_test(RNGContext *rng, const PRUint8 *entropy, | |
| 232 unsigned int entropy_len, const PRUint8 *additional_input, | |
| 233 unsigned int additional_input_len) | |
| 234 { | |
| 235 SECStatus rv; | |
| 236 | |
| 237 /* do health checks in FIPS mode */ | |
| 238 rv = PRNGTEST_RunHealthTests(); | |
| 239 if (rv != SECSuccess) { | |
| 240 /* error set by PRNGTEST_RunHealTests() */ | |
| 241 rng->isValid = PR_FALSE; | |
| 242 return SECFailure; | |
| 243 } | |
| 244 return prng_reseed(rng, entropy, entropy_len, | |
| 245 additional_input, additional_input_len); | |
| 246 } | |
| 247 | |
| 248 /* | |
| 249 * build some fast inline functions for adding. | |
| 250 */ | |
| 251 #define PRNG_ADD_CARRY_ONLY(dest, start, cy) \ | |
| 252 carry = cy; \ | |
| 253 for (k1=start; carry && k1 >=0 ; k1--) { \ | |
| 254 carry = !(++dest[k1]); \ | |
| 255 } | |
| 256 | |
| 257 /* | |
| 258 * NOTE: dest must be an array for the following to work. | |
| 259 */ | |
| 260 #define PRNG_ADD_BITS(dest, dest_len, add, len) \ | |
| 261 carry = 0; \ | |
| 262 for (k1=dest_len -1, k2=len-1; k2 >= 0; --k1, --k2) { \ | |
| 263 carry += dest[k1]+ add[k2]; \ | |
| 264 dest[k1] = (PRUint8) carry; \ | |
| 265 carry >>= 8; \ | |
| 266 } | |
| 267 | |
| 268 #define PRNG_ADD_BITS_AND_CARRY(dest, dest_len, add, len) \ | |
| 269 PRNG_ADD_BITS(dest, dest_len, add, len) \ | |
| 270 PRNG_ADD_CARRY_ONLY(dest, k1, carry) | |
| 271 | |
| 272 /* | |
| 273 * This function expands the internal state of the prng to fulfill any number | |
| 274 * of bytes we need for this request. We only use this call if we need more | |
| 275 * than can be supplied by a single call to SHA256_HashBuf. | |
| 276 * | |
| 277 * This function is specified in NIST SP 800-90 section 10.1.1.4, Hashgen | |
| 278 */ | |
| 279 static void | |
| 280 prng_Hashgen(RNGContext *rng, PRUint8 *returned_bytes, | |
| 281 unsigned int no_of_returned_bytes) | |
| 282 { | |
| 283 PRUint8 data[VSize(rng)]; | |
| 284 | |
| 285 PORT_Memcpy(data, V(rng), VSize(rng)); | |
| 286 while (no_of_returned_bytes) { | |
| 287 SHA256Context ctx; | |
| 288 unsigned int len; | |
| 289 unsigned int carry; | |
| 290 int k1; | |
| 291 | |
| 292 SHA256_Begin(&ctx); | |
| 293 SHA256_Update(&ctx, data, sizeof data); | |
| 294 SHA256_End(&ctx, returned_bytes, &len, no_of_returned_bytes); | |
| 295 returned_bytes += len; | |
| 296 no_of_returned_bytes -= len; | |
| 297 /* The carry parameter is a bool (increment or not). | |
| 298 * This increments data if no_of_returned_bytes is not zero */ | |
| 299 PRNG_ADD_CARRY_ONLY(data, (sizeof data)- 1, no_of_returned_bytes); | |
| 300 } | |
| 301 PORT_Memset(data, 0, sizeof data); | |
| 302 } | |
| 303 | |
| 304 /* | |
| 305 * Generates new random bytes and advances the internal prng state. | |
| 306 * additional bytes are only used in algorithm testing. | |
| 307 * | |
| 308 * This function is specified in NIST SP 800-90 section 10.1.1.4 | |
| 309 */ | |
| 310 static SECStatus | |
| 311 prng_generateNewBytes(RNGContext *rng, | |
| 312 PRUint8 *returned_bytes, unsigned int no_of_returned_bytes, | |
| 313 const PRUint8 *additional_input, | |
| 314 unsigned int additional_input_len) | |
| 315 { | |
| 316 PRUint8 H[SHA256_LENGTH]; /* both H and w since they | |
| 317 * aren't used concurrently */ | |
| 318 unsigned int carry; | |
| 319 int k1, k2; | |
| 320 | |
| 321 if (!rng->isValid) { | |
| 322 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); | |
| 323 return SECFailure; | |
| 324 } | |
| 325 /* This code only triggers during tests, normal | |
| 326 * prng operation does not use additional_input */ | |
| 327 if (additional_input){ | |
| 328 SHA256Context ctx; | |
| 329 /* NIST SP 800-90 defines two temporaries in their calculations, | |
| 330 * w and H. These temporaries are the same lengths, and used | |
| 331 * at different times, so we use the following macro to collapse | |
| 332 * them to the same variable, but keeping their unique names for | |
| 333 * easy comparison to the spec */ | |
| 334 #define w H | |
| 335 rng->V_type = prngAdditionalDataType; | |
| 336 SHA256_Begin(&ctx); | |
| 337 SHA256_Update(&ctx, rng->V_Data, sizeof rng->V_Data); | |
| 338 SHA256_Update(&ctx, additional_input, additional_input_len); | |
| 339 SHA256_End(&ctx, w, NULL, sizeof w); | |
| 340 PRNG_ADD_BITS_AND_CARRY(V(rng), VSize(rng), w, sizeof w) | |
| 341 PORT_Memset(w, 0, sizeof w); | |
| 342 #undef w | |
| 343 } | |
| 344 | |
| 345 if (no_of_returned_bytes == SHA256_LENGTH) { | |
| 346 /* short_cut to hashbuf and save a copy and a clear */ | |
| 347 SHA256_HashBuf(returned_bytes, V(rng), VSize(rng) ); | |
| 348 } else { | |
| 349 prng_Hashgen(rng, returned_bytes, no_of_returned_bytes); | |
| 350 } | |
| 351 /* advance our internal state... */ | |
| 352 rng->V_type = prngGenerateByteType; | |
| 353 SHA256_HashBuf(H, rng->V_Data, sizeof rng->V_Data); | |
| 354 PRNG_ADD_BITS_AND_CARRY(V(rng), VSize(rng), H, sizeof H) | |
| 355 PRNG_ADD_BITS(V(rng), VSize(rng), rng->C, sizeof rng->C); | |
| 356 PRNG_ADD_BITS_AND_CARRY(V(rng), VSize(rng), rng->reseed_counter, | |
| 357 sizeof rng->reseed_counter) | |
| 358 PRNG_ADD_CARRY_ONLY(rng->reseed_counter,(sizeof rng->reseed_counter)-1, 1); | |
| 359 | |
| 360 /* continuous rng check */ | |
| 361 if (memcmp(V(rng), rng->oldV, sizeof rng->oldV) == 0) { | |
| 362 rng->isValid = PR_FALSE; | |
| 363 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); | |
| 364 return SECFailure; | |
| 365 } | |
| 366 PORT_Memcpy(rng->oldV, V(rng), sizeof rng->oldV); | |
| 367 return SECSuccess; | |
| 368 } | |
| 369 | |
| 370 /* Use NSPR to prevent RNG_RNGInit from being called from separate | |
| 371 * threads, creating a race condition. | |
| 372 */ | |
| 373 static const PRCallOnceType pristineCallOnce; | |
| 374 static PRCallOnceType coRNGInit; | |
| 375 static PRStatus rng_init(void) | |
| 376 { | |
| 377 PRUint8 bytes[PRNG_SEEDLEN*2]; /* entropy + nonce */ | |
| 378 unsigned int numBytes; | |
| 379 SECStatus rv = SECSuccess; | |
| 380 | |
| 381 if (globalrng == NULL) { | |
| 382 /* bytes needs to have enough space to hold | |
| 383 * a SHA256 hash value. Blow up at compile time if this isn't true */ | |
| 384 PR_STATIC_ASSERT(sizeof(bytes) >= SHA256_LENGTH); | |
| 385 /* create a new global RNG context */ | |
| 386 globalrng = &theGlobalRng; | |
| 387 PORT_Assert(NULL == globalrng->lock); | |
| 388 /* create a lock for it */ | |
| 389 globalrng->lock = PZ_NewLock(nssILockOther); | |
| 390 if (globalrng->lock == NULL) { | |
| 391 globalrng = NULL; | |
| 392 PORT_SetError(PR_OUT_OF_MEMORY_ERROR); | |
| 393 return PR_FAILURE; | |
| 394 } | |
| 395 | |
| 396 /* Try to get some seed data for the RNG */ | |
| 397 numBytes = (unsigned int) RNG_SystemRNG(bytes, sizeof bytes); | |
| 398 PORT_Assert(numBytes == 0 || numBytes == sizeof bytes); | |
| 399 if (numBytes != 0) { | |
| 400 /* if this is our first call, instantiate, otherwise reseed | |
| 401 * prng_instantiate gets a new clean state, we want to mix | |
| 402 * any previous entropy we may have collected */ | |
| 403 if (V(globalrng)[0] == 0) { | |
| 404 rv = prng_instantiate(globalrng, bytes, numBytes); | |
| 405 } else { | |
| 406 rv = prng_reseed_test(globalrng, bytes, numBytes, NULL, 0); | |
| 407 } | |
| 408 memset(bytes, 0, numBytes); | |
| 409 } else { | |
| 410 PZ_DestroyLock(globalrng->lock); | |
| 411 globalrng->lock = NULL; | |
| 412 globalrng = NULL; | |
| 413 return PR_FAILURE; | |
| 414 } | |
| 415 | |
| 416 if (rv != SECSuccess) { | |
| 417 return PR_FAILURE; | |
| 418 } | |
| 419 /* the RNG is in a valid state */ | |
| 420 globalrng->isValid = PR_TRUE; | |
| 421 | |
| 422 /* fetch one random value so that we can populate rng->oldV for our | |
| 423 * continous random number test. */ | |
| 424 prng_generateNewBytes(globalrng, bytes, SHA256_LENGTH, NULL, 0); | |
| 425 | |
| 426 /* Fetch more entropy into the PRNG */ | |
| 427 RNG_SystemInfoForRNG(); | |
| 428 } | |
| 429 return PR_SUCCESS; | |
| 430 } | |
| 431 | |
| 432 /* | |
| 433 * Clean up the global RNG context | |
| 434 */ | |
| 435 static void | |
| 436 prng_freeRNGContext(RNGContext *rng) | |
| 437 { | |
| 438 PRUint8 inputhash[VSize(rng) + (sizeof rng->C)]; | |
| 439 | |
| 440 /* destroy context lock */ | |
| 441 SKIP_AFTER_FORK(PZ_DestroyLock(globalrng->lock)); | |
| 442 | |
| 443 /* zero global RNG context except for C & V to preserve entropy */ | |
| 444 prng_Hash_df(inputhash, sizeof rng->C, rng->C, sizeof rng->C, NULL, 0); | |
| 445 prng_Hash_df(&inputhash[sizeof rng->C], VSize(rng), V(rng), VSize(rng), | |
| 446 NULL, 0); | |
| 447 memset(rng, 0, sizeof *rng); | |
| 448 memcpy(rng->C, inputhash, sizeof rng->C); | |
| 449 memcpy(V(rng), &inputhash[sizeof rng->C], VSize(rng)); | |
| 450 | |
| 451 memset(inputhash, 0, sizeof inputhash); | |
| 452 } | |
| 453 | |
| 454 /* | |
| 455 * Public functions | |
| 456 */ | |
| 457 | |
| 458 /* | |
| 459 * Initialize the global RNG context and give it some seed input taken | |
| 460 * from the system. This function is thread-safe and will only allow | |
| 461 * the global context to be initialized once. The seed input is likely | |
| 462 * small, so it is imperative that RNG_RandomUpdate() be called with | |
| 463 * additional seed data before the generator is used. A good way to | |
| 464 * provide the generator with additional entropy is to call | |
| 465 * RNG_SystemInfoForRNG(). Note that C_Initialize() does exactly that. | |
| 466 */ | |
| 467 SECStatus | |
| 468 RNG_RNGInit(void) | |
| 469 { | |
| 470 /* Allow only one call to initialize the context */ | |
| 471 PR_CallOnce(&coRNGInit, rng_init); | |
| 472 /* Make sure there is a context */ | |
| 473 return (globalrng != NULL) ? SECSuccess : SECFailure; | |
| 474 } | |
| 475 | |
| 476 /* | |
| 477 ** Update the global random number generator with more seeding | |
| 478 ** material. | |
| 479 */ | |
| 480 SECStatus | |
| 481 RNG_RandomUpdate(const void *data, size_t bytes) | |
| 482 { | |
| 483 SECStatus rv; | |
| 484 | |
| 485 /* Make sure our assumption that size_t is unsigned is true */ | |
| 486 PR_STATIC_ASSERT(((size_t)-1) > (size_t)1); | |
| 487 | |
| 488 #if defined(NS_PTR_GT_32) || (defined(NSS_USE_64) && !defined(NS_PTR_LE_32)) | |
| 489 /* | |
| 490 * NIST 800-90 requires us to verify our inputs. This value can | |
| 491 * come from the application, so we need to make sure it's within the | |
| 492 * spec. The spec says it must be less than 2^32 bytes (2^35 bits). | |
| 493 * This can only happen if size_t is greater than 32 bits (i.e. on | |
| 494 * most 64 bit platforms). The 90% case (perhaps 100% case), size_t | |
| 495 * is less than or equal to 32 bits if the platform is not 64 bits, and | |
| 496 * greater than 32 bits if it is a 64 bit platform. The corner | |
| 497 * cases are handled with explicit defines NS_PTR_GT_32 and NS_PTR_LE_32. | |
| 498 * | |
| 499 * In general, neither NS_PTR_GT_32 nor NS_PTR_LE_32 will need to be | |
| 500 * defined. If you trip over the next two size ASSERTS at compile time, | |
| 501 * you will need to define them for your platform. | |
| 502 * | |
| 503 * if 'sizeof(size_t) > 4' is triggered it means that we were expecting | |
| 504 * sizeof(size_t) to be greater than 4, but it wasn't. Setting | |
| 505 * NS_PTR_LE_32 will correct that mistake. | |
| 506 * | |
| 507 * if 'sizeof(size_t) <= 4' is triggered, it means that we were expecting | |
| 508 * sizeof(size_t) to be less than or equal to 4, but it wasn't. Setting | |
| 509 * NS_PTR_GT_32 will correct that mistake. | |
| 510 */ | |
| 511 | |
| 512 PR_STATIC_ASSERT(sizeof(size_t) > 4); | |
| 513 | |
| 514 if (bytes > PRNG_MAX_ADDITIONAL_BYTES) { | |
| 515 bytes = PRNG_MAX_ADDITIONAL_BYTES; | |
| 516 } | |
| 517 #else | |
| 518 PR_STATIC_ASSERT(sizeof(size_t) <= 4); | |
| 519 #endif | |
| 520 | |
| 521 PZ_Lock(globalrng->lock); | |
| 522 /* if we're passed more than our additionalDataCache, simply | |
| 523 * call reseed with that data */ | |
| 524 if (bytes > sizeof (globalrng->additionalDataCache)) { | |
| 525 rv = prng_reseed_test(globalrng, NULL, 0, data, (unsigned int) bytes); | |
| 526 /* if we aren't going to fill or overflow the buffer, just cache it */ | |
| 527 } else if (bytes < ((sizeof globalrng->additionalDataCache) | |
| 528 - globalrng->additionalAvail)) { | |
| 529 PORT_Memcpy(globalrng->additionalDataCache+globalrng->additionalAvail, | |
| 530 data, bytes); | |
| 531 globalrng->additionalAvail += (PRUint32) bytes; | |
| 532 rv = SECSuccess; | |
| 533 } else { | |
| 534 /* we are going to fill or overflow the buffer. In this case we will | |
| 535 * fill the entropy buffer, reseed with it, start a new buffer with the | |
| 536 * remainder. We know the remainder will fit in the buffer because | |
| 537 * we already handled the case where bytes > the size of the buffer. | |
| 538 */ | |
| 539 size_t bufRemain = (sizeof globalrng->additionalDataCache) | |
| 540 - globalrng->additionalAvail; | |
| 541 /* fill the rest of the buffer */ | |
| 542 if (bufRemain) { | |
| 543 PORT_Memcpy(globalrng->additionalDataCache | |
| 544 +globalrng->additionalAvail, | |
| 545 data, bufRemain); | |
| 546 data = ((unsigned char *)data) + bufRemain; | |
| 547 bytes -= bufRemain; | |
| 548 } | |
| 549 /* reseed from buffer */ | |
| 550 rv = prng_reseed_test(globalrng, NULL, 0, | |
| 551 globalrng->additionalDataCache, | |
| 552 sizeof globalrng->additionalDataCache); | |
| 553 | |
| 554 /* copy the rest into the cache */ | |
| 555 PORT_Memcpy(globalrng->additionalDataCache, data, bytes); | |
| 556 globalrng->additionalAvail = (PRUint32) bytes; | |
| 557 } | |
| 558 | |
| 559 PZ_Unlock(globalrng->lock); | |
| 560 return rv; | |
| 561 } | |
| 562 | |
| 563 /* | |
| 564 ** Generate some random bytes, using the global random number generator | |
| 565 ** object. | |
| 566 */ | |
| 567 static SECStatus | |
| 568 prng_GenerateGlobalRandomBytes(RNGContext *rng, | |
| 569 void *dest, size_t len) | |
| 570 { | |
| 571 SECStatus rv = SECSuccess; | |
| 572 PRUint8 *output = dest; | |
| 573 /* check for a valid global RNG context */ | |
| 574 PORT_Assert(rng != NULL); | |
| 575 if (rng == NULL) { | |
| 576 PORT_SetError(SEC_ERROR_INVALID_ARGS); | |
| 577 return SECFailure; | |
| 578 } | |
| 579 /* FIPS limits the amount of entropy available in a single request */ | |
| 580 if (len > PRNG_MAX_REQUEST_SIZE) { | |
| 581 PORT_SetError(SEC_ERROR_INVALID_ARGS); | |
| 582 return SECFailure; | |
| 583 } | |
| 584 /* --- LOCKED --- */ | |
| 585 PZ_Lock(rng->lock); | |
| 586 /* Check the amount of seed data in the generator. If not enough, | |
| 587 * don't produce any data. | |
| 588 */ | |
| 589 if (rng->reseed_counter[0] >= RESEED_VALUE) { | |
| 590 rv = prng_reseed_test(rng, NULL, 0, NULL, 0); | |
| 591 PZ_Unlock(rng->lock); | |
| 592 if (rv != SECSuccess) { | |
| 593 return rv; | |
| 594 } | |
| 595 RNG_SystemInfoForRNG(); | |
| 596 PZ_Lock(rng->lock); | |
| 597 } | |
| 598 /* | |
| 599 * see if we have enough bytes to fulfill the request. | |
| 600 */ | |
| 601 if (len <= rng->dataAvail) { | |
| 602 memcpy(output, rng->data + ((sizeof rng->data) - rng->dataAvail), len); | |
| 603 memset(rng->data + ((sizeof rng->data) - rng->dataAvail), 0, len); | |
| 604 rng->dataAvail -= len; | |
| 605 rv = SECSuccess; | |
| 606 /* if we are asking for a small number of bytes, cache the rest of | |
| 607 * the bytes */ | |
| 608 } else if (len < sizeof rng->data) { | |
| 609 rv = prng_generateNewBytes(rng, rng->data, sizeof rng->data, | |
| 610 rng->additionalAvail ? rng->additionalDataCache : NULL, | |
| 611 rng->additionalAvail); | |
| 612 rng->additionalAvail = 0; | |
| 613 if (rv == SECSuccess) { | |
| 614 memcpy(output, rng->data, len); | |
| 615 memset(rng->data, 0, len); | |
| 616 rng->dataAvail = (sizeof rng->data) - len; | |
| 617 } | |
| 618 /* we are asking for lots of bytes, just ask the generator to pass them */ | |
| 619 } else { | |
| 620 rv = prng_generateNewBytes(rng, output, len, | |
| 621 rng->additionalAvail ? rng->additionalDataCache : NULL, | |
| 622 rng->additionalAvail); | |
| 623 rng->additionalAvail = 0; | |
| 624 } | |
| 625 PZ_Unlock(rng->lock); | |
| 626 /* --- UNLOCKED --- */ | |
| 627 return rv; | |
| 628 } | |
| 629 | |
| 630 /* | |
| 631 ** Generate some random bytes, using the global random number generator | |
| 632 ** object. | |
| 633 */ | |
| 634 SECStatus | |
| 635 RNG_GenerateGlobalRandomBytes(void *dest, size_t len) | |
| 636 { | |
| 637 return prng_GenerateGlobalRandomBytes(globalrng, dest, len); | |
| 638 } | |
| 639 | |
| 640 void | |
| 641 RNG_RNGShutdown(void) | |
| 642 { | |
| 643 /* check for a valid global RNG context */ | |
| 644 PORT_Assert(globalrng != NULL); | |
| 645 if (globalrng == NULL) { | |
| 646 /* Should set a "not initialized" error code. */ | |
| 647 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
| 648 return; | |
| 649 } | |
| 650 /* clear */ | |
| 651 prng_freeRNGContext(globalrng); | |
| 652 globalrng = NULL; | |
| 653 /* reset the callonce struct to allow a new call to RNG_RNGInit() */ | |
| 654 coRNGInit = pristineCallOnce; | |
| 655 } | |
| 656 | |
| 657 /* | |
| 658 * Test case interface. used by fips testing and power on self test | |
| 659 */ | |
| 660 /* make sure the test context is separate from the global context, This | |
| 661 * allows us to test the internal random number generator without losing | |
| 662 * entropy we may have previously collected. */ | |
| 663 RNGContext testContext; | |
| 664 | |
| 665 /* | |
| 666 * Test vector API. Use NIST SP 800-90 general interface so one of the | |
| 667 * other NIST SP 800-90 algorithms may be used in the future. | |
| 668 */ | |
| 669 SECStatus | |
| 670 PRNGTEST_Instantiate(const PRUint8 *entropy, unsigned int entropy_len, | |
| 671 const PRUint8 *nonce, unsigned int nonce_len, | |
| 672 const PRUint8 *personal_string, unsigned int ps_len) | |
| 673 { | |
| 674 int bytes_len = entropy_len + nonce_len + ps_len; | |
| 675 PRUint8 *bytes = NULL; | |
| 676 SECStatus rv; | |
| 677 | |
| 678 if (entropy_len < 256/PR_BITS_PER_BYTE) { | |
| 679 PORT_SetError(SEC_ERROR_NEED_RANDOM); | |
| 680 return SECFailure; | |
| 681 } | |
| 682 | |
| 683 bytes = PORT_Alloc(bytes_len); | |
| 684 if (bytes == NULL) { | |
| 685 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
| 686 return SECFailure; | |
| 687 } | |
| 688 /* concatenate the various inputs, internally NSS only instantiates with | |
| 689 * a single long string */ | |
| 690 PORT_Memcpy(bytes, entropy, entropy_len); | |
| 691 if (nonce) { | |
| 692 PORT_Memcpy(&bytes[entropy_len], nonce, nonce_len); | |
| 693 } else { | |
| 694 PORT_Assert(nonce_len == 0); | |
| 695 } | |
| 696 if (personal_string) { | |
| 697 PORT_Memcpy(&bytes[entropy_len+nonce_len], personal_string, ps_len); | |
| 698 } else { | |
| 699 PORT_Assert(ps_len == 0); | |
| 700 } | |
| 701 rv = prng_instantiate(&testContext, bytes, bytes_len); | |
| 702 PORT_ZFree(bytes, bytes_len); | |
| 703 if (rv == SECFailure) { | |
| 704 return SECFailure; | |
| 705 } | |
| 706 testContext.isValid = PR_TRUE; | |
| 707 return SECSuccess; | |
| 708 } | |
| 709 | |
| 710 SECStatus | |
| 711 PRNGTEST_Reseed(const PRUint8 *entropy, unsigned int entropy_len, | |
| 712 const PRUint8 *additional, unsigned int additional_len) | |
| 713 { | |
| 714 if (!testContext.isValid) { | |
| 715 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); | |
| 716 return SECFailure; | |
| 717 } | |
| 718 /* This magic input tells us to set the reseed count to it's max count, | |
| 719 * so we can simulate PRNGTEST_Generate reaching max reseed count */ | |
| 720 if ((entropy == NULL) && (entropy_len == 0) && | |
| 721 (additional == NULL) && (additional_len == 0)) { | |
| 722 testContext.reseed_counter[0] = RESEED_VALUE; | |
| 723 return SECSuccess; | |
| 724 } | |
| 725 return prng_reseed(&testContext, entropy, entropy_len, additional, | |
| 726 additional_len); | |
| 727 | |
| 728 } | |
| 729 | |
| 730 SECStatus | |
| 731 PRNGTEST_Generate(PRUint8 *bytes, unsigned int bytes_len, | |
| 732 const PRUint8 *additional, unsigned int additional_len) | |
| 733 { | |
| 734 SECStatus rv; | |
| 735 if (!testContext.isValid) { | |
| 736 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); | |
| 737 return SECFailure; | |
| 738 } | |
| 739 /* replicate reseed test from prng_GenerateGlobalRandomBytes */ | |
| 740 if (testContext.reseed_counter[0] >= RESEED_VALUE) { | |
| 741 rv = prng_reseed(&testContext, NULL, 0, NULL, 0); | |
| 742 if (rv != SECSuccess) { | |
| 743 return rv; | |
| 744 } | |
| 745 } | |
| 746 return prng_generateNewBytes(&testContext, bytes, bytes_len, | |
| 747 additional, additional_len); | |
| 748 | |
| 749 } | |
| 750 | |
| 751 SECStatus | |
| 752 PRNGTEST_Uninstantiate() | |
| 753 { | |
| 754 if (!testContext.isValid) { | |
| 755 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); | |
| 756 return SECFailure; | |
| 757 } | |
| 758 PORT_Memset(&testContext, 0, sizeof testContext); | |
| 759 return SECSuccess; | |
| 760 } | |
| 761 | |
| 762 SECStatus | |
| 763 PRNGTEST_RunHealthTests() | |
| 764 { | |
| 765 static const PRUint8 entropy[] = { | |
| 766 0x8e,0x9c,0x0d,0x25,0x75,0x22,0x04,0xf9, | |
| 767 0xc5,0x79,0x10,0x8b,0x23,0x79,0x37,0x14, | |
| 768 0x9f,0x2c,0xc7,0x0b,0x39,0xf8,0xee,0xef, | |
| 769 0x95,0x0c,0x97,0x59,0xfc,0x0a,0x85,0x41, | |
| 770 0x76,0x9d,0x6d,0x67,0x00,0x4e,0x19,0x12, | |
| 771 0x02,0x16,0x53,0xea,0xf2,0x73,0xd7,0xd6, | |
| 772 0x7f,0x7e,0xc8,0xae,0x9c,0x09,0x99,0x7d, | |
| 773 0xbb,0x9e,0x48,0x7f,0xbb,0x96,0x46,0xb3, | |
| 774 0x03,0x75,0xf8,0xc8,0x69,0x45,0x3f,0x97, | |
| 775 0x5e,0x2e,0x48,0xe1,0x5d,0x58,0x97,0x4c }; | |
| 776 static const PRUint8 rng_known_result[] = { | |
| 777 0x16,0xe1,0x8c,0x57,0x21,0xd8,0xf1,0x7e, | |
| 778 0x5a,0xa0,0x16,0x0b,0x7e,0xa6,0x25,0xb4, | |
| 779 0x24,0x19,0xdb,0x54,0xfa,0x35,0x13,0x66, | |
| 780 0xbb,0xaa,0x2a,0x1b,0x22,0x33,0x2e,0x4a, | |
| 781 0x14,0x07,0x9d,0x52,0xfc,0x73,0x61,0x48, | |
| 782 0xac,0xc1,0x22,0xfc,0xa4,0xfc,0xac,0xa4, | |
| 783 0xdb,0xda,0x5b,0x27,0x33,0xc4,0xb3 }; | |
| 784 static const PRUint8 reseed_entropy[] = { | |
| 785 0xc6,0x0b,0x0a,0x30,0x67,0x07,0xf4,0xe2, | |
| 786 0x24,0xa7,0x51,0x6f,0x5f,0x85,0x3e,0x5d, | |
| 787 0x67,0x97,0xb8,0x3b,0x30,0x9c,0x7a,0xb1, | |
| 788 0x52,0xc6,0x1b,0xc9,0x46,0xa8,0x62,0x79 }; | |
| 789 static const PRUint8 additional_input[] = { | |
| 790 0x86,0x82,0x28,0x98,0xe7,0xcb,0x01,0x14, | |
| 791 0xae,0x87,0x4b,0x1d,0x99,0x1b,0xc7,0x41, | |
| 792 0x33,0xff,0x33,0x66,0x40,0x95,0x54,0xc6, | |
| 793 0x67,0x4d,0x40,0x2a,0x1f,0xf9,0xeb,0x65 }; | |
| 794 static const PRUint8 rng_reseed_result[] = { | |
| 795 0x02,0x0c,0xc6,0x17,0x86,0x49,0xba,0xc4, | |
| 796 0x7b,0x71,0x35,0x05,0xf0,0xdb,0x4a,0xc2, | |
| 797 0x2c,0x38,0xc1,0xa4,0x42,0xe5,0x46,0x4a, | |
| 798 0x7d,0xf0,0xbe,0x47,0x88,0xb8,0x0e,0xc6, | |
| 799 0x25,0x2b,0x1d,0x13,0xef,0xa6,0x87,0x96, | |
| 800 0xa3,0x7d,0x5b,0x80,0xc2,0x38,0x76,0x61, | |
| 801 0xc7,0x80,0x5d,0x0f,0x05,0x76,0x85 }; | |
| 802 static const PRUint8 rng_no_reseed_result[] = { | |
| 803 0xc4,0x40,0x41,0x8c,0xbf,0x2f,0x70,0x23, | |
| 804 0x88,0xf2,0x7b,0x30,0xc3,0xca,0x1e,0xf3, | |
| 805 0xef,0x53,0x81,0x5d,0x30,0xed,0x4c,0xf1, | |
| 806 0xff,0x89,0xa5,0xee,0x92,0xf8,0xc0,0x0f, | |
| 807 0x88,0x53,0xdf,0xb6,0x76,0xf0,0xaa,0xd3, | |
| 808 0x2e,0x1d,0x64,0x37,0x3e,0xe8,0x4a,0x02, | |
| 809 0xff,0x0a,0x7f,0xe5,0xe9,0x2b,0x6d }; | |
| 810 | |
| 811 SECStatus rng_status = SECSuccess; | |
| 812 PR_STATIC_ASSERT(sizeof(rng_known_result) >= sizeof(rng_reseed_result)); | |
| 813 PRUint8 result[sizeof(rng_known_result)]; | |
| 814 | |
| 815 /********************************************/ | |
| 816 /* First test instantiate error path. */ | |
| 817 /* In this case we supply enough entropy, */ | |
| 818 /* but not enough seed. This will trigger */ | |
| 819 /* the code that checks for a entropy */ | |
| 820 /* source failure. */ | |
| 821 /********************************************/ | |
| 822 rng_status = PRNGTEST_Instantiate(entropy, 256/PR_BITS_PER_BYTE, | |
| 823 NULL, 0, NULL, 0); | |
| 824 if (rng_status == SECSuccess) { | |
| 825 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); | |
| 826 return SECFailure; | |
| 827 } | |
| 828 if (PORT_GetError() != SEC_ERROR_NEED_RANDOM) { | |
| 829 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); | |
| 830 return SECFailure; | |
| 831 } | |
| 832 /* we failed with the proper error code, we can continue */ | |
| 833 | |
| 834 /********************************************/ | |
| 835 /* Generate random bytes with a known seed. */ | |
| 836 /********************************************/ | |
| 837 rng_status = PRNGTEST_Instantiate(entropy, sizeof entropy, | |
| 838 NULL, 0, NULL, 0); | |
| 839 if (rng_status != SECSuccess) { | |
| 840 /* Error set by PRNGTEST_Instantiate */ | |
| 841 return SECFailure; | |
| 842 } | |
| 843 rng_status = PRNGTEST_Generate(result, sizeof rng_known_result, NULL, 0); | |
| 844 if ( ( rng_status != SECSuccess) || | |
| 845 ( PORT_Memcmp( result, rng_known_result, | |
| 846 sizeof rng_known_result ) != 0 ) ) { | |
| 847 PRNGTEST_Uninstantiate(); | |
| 848 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); | |
| 849 return SECFailure; | |
| 850 } | |
| 851 rng_status = PRNGTEST_Reseed(reseed_entropy, sizeof reseed_entropy, | |
| 852 additional_input, sizeof additional_input); | |
| 853 if (rng_status != SECSuccess) { | |
| 854 /* Error set by PRNG_Reseed */ | |
| 855 PRNGTEST_Uninstantiate(); | |
| 856 return SECFailure; | |
| 857 } | |
| 858 rng_status = PRNGTEST_Generate(result, sizeof rng_reseed_result, NULL, 0); | |
| 859 if ( ( rng_status != SECSuccess) || | |
| 860 ( PORT_Memcmp( result, rng_reseed_result, | |
| 861 sizeof rng_reseed_result ) != 0 ) ) { | |
| 862 PRNGTEST_Uninstantiate(); | |
| 863 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); | |
| 864 return SECFailure; | |
| 865 } | |
| 866 /* This magic forces the reseed count to it's max count, so we can see if | |
| 867 * PRNGTEST_Generate will actually when it reaches it's count */ | |
| 868 rng_status = PRNGTEST_Reseed(NULL, 0, NULL, 0); | |
| 869 if (rng_status != SECSuccess) { | |
| 870 PRNGTEST_Uninstantiate(); | |
| 871 /* Error set by PRNG_Reseed */ | |
| 872 return SECFailure; | |
| 873 } | |
| 874 /* This generate should now reseed */ | |
| 875 rng_status = PRNGTEST_Generate(result, sizeof rng_reseed_result, NULL, 0); | |
| 876 if ( ( rng_status != SECSuccess) || | |
| 877 /* NOTE we fail if the result is equal to the no_reseed_result. | |
| 878 * no_reseed_result is the value we would have gotten if we didn't | |
| 879 * do an automatic reseed in PRNGTEST_Generate */ | |
| 880 ( PORT_Memcmp( result, rng_no_reseed_result, | |
| 881 sizeof rng_no_reseed_result ) == 0 ) ) { | |
| 882 PRNGTEST_Uninstantiate(); | |
| 883 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); | |
| 884 return SECFailure; | |
| 885 } | |
| 886 /* make sure reseed fails when we don't supply enough entropy */ | |
| 887 rng_status = PRNGTEST_Reseed(reseed_entropy, 4, NULL, 0); | |
| 888 if (rng_status == SECSuccess) { | |
| 889 PRNGTEST_Uninstantiate(); | |
| 890 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); | |
| 891 return SECFailure; | |
| 892 } | |
| 893 if (PORT_GetError() != SEC_ERROR_NEED_RANDOM) { | |
| 894 PRNGTEST_Uninstantiate(); | |
| 895 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); | |
| 896 return SECFailure; | |
| 897 } | |
| 898 rng_status = PRNGTEST_Uninstantiate(); | |
| 899 if (rng_status != SECSuccess) { | |
| 900 /* Error set by PRNG_Uninstantiate */ | |
| 901 return rng_status; | |
| 902 } | |
| 903 /* make sure uninstantiate fails if the contest is not initiated (also tests | |
| 904 * if the context was cleared in the previous Uninstantiate) */ | |
| 905 rng_status = PRNGTEST_Uninstantiate(); | |
| 906 if (rng_status == SECSuccess) { | |
| 907 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); | |
| 908 return SECFailure; | |
| 909 } | |
| 910 if (PORT_GetError() != SEC_ERROR_LIBRARY_FAILURE) { | |
| 911 return rng_status; | |
| 912 } | |
| 913 | |
| 914 return SECSuccess; | |
| 915 } | |
| OLD | NEW |