1'use strict'; 2 3const { 4 MathMin, 5 NumberIsNaN, 6 NumberIsSafeInteger 7} = primordials; 8 9const { AsyncWrap, Providers } = internalBinding('async_wrap'); 10const { 11 Buffer, 12 kMaxLength, 13} = require('buffer'); 14const { 15 randomBytes: _randomBytes, 16 secureBuffer, 17} = internalBinding('crypto'); 18const { 19 codes: { 20 ERR_INVALID_ARG_TYPE, 21 ERR_INVALID_CALLBACK, 22 ERR_OUT_OF_RANGE, 23 ERR_OPERATION_FAILED, 24 } 25} = require('internal/errors'); 26const { 27 validateBoolean, 28 validateNumber, 29 validateObject, 30} = require('internal/validators'); 31const { isArrayBufferView } = require('internal/util/types'); 32const { FastBuffer } = require('internal/buffer'); 33 34const kMaxInt32 = 2 ** 31 - 1; 35const kMaxPossibleLength = MathMin(kMaxLength, kMaxInt32); 36 37function assertOffset(offset, elementSize, length) { 38 validateNumber(offset, 'offset'); 39 offset *= elementSize; 40 41 const maxLength = MathMin(length, kMaxPossibleLength); 42 if (NumberIsNaN(offset) || offset > maxLength || offset < 0) { 43 throw new ERR_OUT_OF_RANGE('offset', `>= 0 && <= ${maxLength}`, offset); 44 } 45 46 return offset >>> 0; // Convert to uint32. 47} 48 49function assertSize(size, elementSize, offset, length) { 50 validateNumber(size, 'size'); 51 size *= elementSize; 52 53 if (NumberIsNaN(size) || size > kMaxPossibleLength || size < 0) { 54 throw new ERR_OUT_OF_RANGE('size', 55 `>= 0 && <= ${kMaxPossibleLength}`, size); 56 } 57 58 if (size + offset > length) { 59 throw new ERR_OUT_OF_RANGE('size + offset', `<= ${length}`, size + offset); 60 } 61 62 return size >>> 0; // Convert to uint32. 63} 64 65function randomBytes(size, callback) { 66 size = assertSize(size, 1, 0, Infinity); 67 if (callback !== undefined && typeof callback !== 'function') 68 throw new ERR_INVALID_CALLBACK(callback); 69 70 const buf = new FastBuffer(size); 71 72 if (!callback) return handleError(_randomBytes(buf, 0, size), buf); 73 74 const wrap = new AsyncWrap(Providers.RANDOMBYTESREQUEST); 75 wrap.ondone = (ex) => { // Retains buf while request is in flight. 76 if (ex) return callback.call(wrap, ex); 77 callback.call(wrap, null, buf); 78 }; 79 80 _randomBytes(buf, 0, size, wrap); 81} 82 83function randomFillSync(buf, offset = 0, size) { 84 if (!isArrayBufferView(buf)) { 85 throw new ERR_INVALID_ARG_TYPE('buf', 'ArrayBufferView', buf); 86 } 87 88 const elementSize = buf.BYTES_PER_ELEMENT || 1; 89 90 offset = assertOffset(offset, elementSize, buf.byteLength); 91 92 if (size === undefined) { 93 size = buf.byteLength - offset; 94 } else { 95 size = assertSize(size, elementSize, offset, buf.byteLength); 96 } 97 98 return handleError(_randomBytes(buf, offset, size), buf); 99} 100 101function randomFill(buf, offset, size, callback) { 102 if (!isArrayBufferView(buf)) { 103 throw new ERR_INVALID_ARG_TYPE('buf', 'ArrayBufferView', buf); 104 } 105 106 const elementSize = buf.BYTES_PER_ELEMENT || 1; 107 108 if (typeof offset === 'function') { 109 callback = offset; 110 offset = 0; 111 size = buf.bytesLength; 112 } else if (typeof size === 'function') { 113 callback = size; 114 size = buf.byteLength - offset; 115 } else if (typeof callback !== 'function') { 116 throw new ERR_INVALID_CALLBACK(callback); 117 } 118 119 offset = assertOffset(offset, elementSize, buf.byteLength); 120 121 if (size === undefined) { 122 size = buf.byteLength - offset; 123 } else { 124 size = assertSize(size, elementSize, offset, buf.byteLength); 125 } 126 127 const wrap = new AsyncWrap(Providers.RANDOMBYTESREQUEST); 128 wrap.ondone = (ex) => { // Retains buf while request is in flight. 129 if (ex) return callback.call(wrap, ex); 130 callback.call(wrap, null, buf); 131 }; 132 133 _randomBytes(buf, offset, size, wrap); 134} 135 136// Largest integer we can read from a buffer. 137// e.g.: Buffer.from("ff".repeat(6), "hex").readUIntBE(0, 6); 138const RAND_MAX = 0xFFFF_FFFF_FFFF; 139 140// Generates an integer in [min, max) range where min is inclusive and max is 141// exclusive. 142function randomInt(min, max, callback) { 143 // Detect optional min syntax 144 // randomInt(max) 145 // randomInt(max, callback) 146 const minNotSpecified = typeof max === 'undefined' || 147 typeof max === 'function'; 148 149 if (minNotSpecified) { 150 callback = max; 151 max = min; 152 min = 0; 153 } 154 155 const isSync = typeof callback === 'undefined'; 156 if (!isSync && typeof callback !== 'function') { 157 throw new ERR_INVALID_CALLBACK(callback); 158 } 159 if (!NumberIsSafeInteger(min)) { 160 throw new ERR_INVALID_ARG_TYPE('min', 'a safe integer', min); 161 } 162 if (!NumberIsSafeInteger(max)) { 163 throw new ERR_INVALID_ARG_TYPE('max', 'a safe integer', max); 164 } 165 if (max <= min) { 166 throw new ERR_OUT_OF_RANGE( 167 'max', `greater than the value of "min" (${min})`, max 168 ); 169 } 170 171 // First we generate a random int between [0..range) 172 const range = max - min; 173 174 if (!(range <= RAND_MAX)) { 175 throw new ERR_OUT_OF_RANGE(`max${minNotSpecified ? '' : ' - min'}`, 176 `<= ${RAND_MAX}`, range); 177 } 178 179 // For (x % range) to produce an unbiased value greater than or equal to 0 and 180 // less than range, x must be drawn randomly from the set of integers greater 181 // than or equal to 0 and less than randLimit. 182 const randLimit = RAND_MAX - (RAND_MAX % range); 183 184 if (isSync) { 185 // Sync API 186 while (true) { 187 const x = randomBytes(6).readUIntBE(0, 6); 188 if (x >= randLimit) { 189 // Try again. 190 continue; 191 } 192 return (x % range) + min; 193 } 194 } else { 195 // Async API 196 const pickAttempt = () => { 197 randomBytes(6, (err, bytes) => { 198 if (err) return callback(err); 199 const x = bytes.readUIntBE(0, 6); 200 if (x >= randLimit) { 201 // Try again. 202 return pickAttempt(); 203 } 204 const n = (x % range) + min; 205 callback(null, n); 206 }); 207 }; 208 209 pickAttempt(); 210 } 211} 212 213function handleError(ex, buf) { 214 if (ex) throw ex; 215 return buf; 216} 217 218// Implements an RFC 4122 version 4 random UUID. 219// To improve performance, random data is generated in batches 220// large enough to cover kBatchSize UUID's at a time. The uuidData 221// and uuid buffers are reused. Each call to randomUUID() consumes 222// 16 bytes from the buffer. 223 224const kHexDigits = [ 225 48, 49, 50, 51, 52, 53, 54, 55, 226 56, 57, 97, 98, 99, 100, 101, 102, 227]; 228 229const kBatchSize = 128; 230let uuidData; 231let uuidNotBuffered; 232let uuid; 233let uuidBatch = 0; 234 235function getBufferedUUID() { 236 if (uuidData === undefined) { 237 uuidData = secureBuffer(16 * kBatchSize); 238 if (uuidData === undefined) 239 throw new ERR_OPERATION_FAILED('Out of memory'); 240 } 241 242 if (uuidBatch === 0) randomFillSync(uuidData); 243 uuidBatch = (uuidBatch + 1) % kBatchSize; 244 return uuidData.slice(uuidBatch * 16, (uuidBatch * 16) + 16); 245} 246 247function randomUUID(options) { 248 if (options !== undefined) 249 validateObject(options, 'options'); 250 const { 251 disableEntropyCache = false, 252 } = { ...options }; 253 254 validateBoolean(disableEntropyCache, 'options.disableEntropyCache'); 255 256 if (uuid === undefined) { 257 uuid = Buffer.alloc(36, '-'); 258 uuid[14] = 52; // '4', identifies the UUID version 259 } 260 261 let uuidBuf; 262 if (!disableEntropyCache) { 263 uuidBuf = getBufferedUUID(); 264 } else { 265 uuidBuf = uuidNotBuffered; 266 if (uuidBuf === undefined) 267 uuidBuf = uuidNotBuffered = secureBuffer(16); 268 if (uuidBuf === undefined) 269 throw new ERR_OPERATION_FAILED('Out of memory'); 270 randomFillSync(uuidBuf); 271 } 272 273 // Variant byte: 10xxxxxx (variant 1) 274 uuidBuf[8] = (uuidBuf[8] & 0x3f) | 0x80; 275 276 // This function is structured the way it is for performance. 277 // The uuid buffer stores the serialization of the random 278 // bytes from uuidData. 279 // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 280 let n = 0; 281 uuid[0] = kHexDigits[uuidBuf[n] >> 4]; 282 uuid[1] = kHexDigits[uuidBuf[n++] & 0xf]; 283 uuid[2] = kHexDigits[uuidBuf[n] >> 4]; 284 uuid[3] = kHexDigits[uuidBuf[n++] & 0xf]; 285 uuid[4] = kHexDigits[uuidBuf[n] >> 4]; 286 uuid[5] = kHexDigits[uuidBuf[n++] & 0xf]; 287 uuid[6] = kHexDigits[uuidBuf[n] >> 4]; 288 uuid[7] = kHexDigits[uuidBuf[n++] & 0xf]; 289 // - 290 uuid[9] = kHexDigits[uuidBuf[n] >> 4]; 291 uuid[10] = kHexDigits[uuidBuf[n++] & 0xf]; 292 uuid[11] = kHexDigits[uuidBuf[n] >> 4]; 293 uuid[12] = kHexDigits[uuidBuf[n++] & 0xf]; 294 // - 295 // 4, uuid[14] is set already... 296 uuid[15] = kHexDigits[uuidBuf[n++] & 0xf]; 297 uuid[16] = kHexDigits[uuidBuf[n] >> 4]; 298 uuid[17] = kHexDigits[uuidBuf[n++] & 0xf]; 299 // - 300 uuid[19] = kHexDigits[uuidBuf[n] >> 4]; 301 uuid[20] = kHexDigits[uuidBuf[n++] & 0xf]; 302 uuid[21] = kHexDigits[uuidBuf[n] >> 4]; 303 uuid[22] = kHexDigits[uuidBuf[n++] & 0xf]; 304 // - 305 uuid[24] = kHexDigits[uuidBuf[n] >> 4]; 306 uuid[25] = kHexDigits[uuidBuf[n++] & 0xf]; 307 uuid[26] = kHexDigits[uuidBuf[n] >> 4]; 308 uuid[27] = kHexDigits[uuidBuf[n++] & 0xf]; 309 uuid[28] = kHexDigits[uuidBuf[n] >> 4]; 310 uuid[29] = kHexDigits[uuidBuf[n++] & 0xf]; 311 uuid[30] = kHexDigits[uuidBuf[n] >> 4]; 312 uuid[31] = kHexDigits[uuidBuf[n++] & 0xf]; 313 uuid[32] = kHexDigits[uuidBuf[n] >> 4]; 314 uuid[33] = kHexDigits[uuidBuf[n++] & 0xf]; 315 uuid[34] = kHexDigits[uuidBuf[n] >> 4]; 316 uuid[35] = kHexDigits[uuidBuf[n] & 0xf]; 317 318 return uuid.latin1Slice(0, 36); 319} 320 321module.exports = { 322 randomBytes, 323 randomFill, 324 randomFillSync, 325 randomInt, 326 randomUUID, 327}; 328