1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22// Flags: --pending-deprecation 23'use strict'; 24const common = require('../common'); 25 26if (!common.hasCrypto) 27 common.skip('missing crypto'); 28 29const assert = require('assert'); 30const crypto = require('crypto'); 31const { kMaxLength } = require('buffer'); 32const { inspect } = require('util'); 33 34const kMaxInt32 = 2 ** 31 - 1; 35const kMaxPossibleLength = Math.min(kMaxLength, kMaxInt32); 36 37common.expectWarning('DeprecationWarning', 38 'crypto.pseudoRandomBytes is deprecated.', 'DEP0115'); 39 40{ 41 [crypto.randomBytes, crypto.pseudoRandomBytes].forEach((f) => { 42 [undefined, null, false, true, {}, []].forEach((value) => { 43 const errObj = { 44 code: 'ERR_INVALID_ARG_TYPE', 45 name: 'TypeError', 46 message: 'The "size" argument must be of type number.' + 47 common.invalidArgTypeHelper(value) 48 }; 49 assert.throws(() => f(value), errObj); 50 assert.throws(() => f(value, common.mustNotCall()), errObj); 51 }); 52 53 [-1, NaN, 2 ** 32, 2 ** 31].forEach((value) => { 54 const errObj = { 55 code: 'ERR_OUT_OF_RANGE', 56 name: 'RangeError', 57 message: 'The value of "size" is out of range. It must be >= 0 && <= ' + 58 `${kMaxPossibleLength}. Received ${value}` 59 }; 60 assert.throws(() => f(value), errObj); 61 assert.throws(() => f(value, common.mustNotCall()), errObj); 62 }); 63 64 [0, 1, 2, 4, 16, 256, 1024, 101.2].forEach((len) => { 65 f(len, common.mustCall((ex, buf) => { 66 assert.strictEqual(ex, null); 67 assert.strictEqual(buf.length, Math.floor(len)); 68 assert.ok(Buffer.isBuffer(buf)); 69 })); 70 }); 71 }); 72} 73 74{ 75 const buf = Buffer.alloc(10); 76 const before = buf.toString('hex'); 77 const after = crypto.randomFillSync(buf).toString('hex'); 78 assert.notStrictEqual(before, after); 79} 80 81{ 82 const buf = new Uint8Array(new Array(10).fill(0)); 83 const before = Buffer.from(buf).toString('hex'); 84 crypto.randomFillSync(buf); 85 const after = Buffer.from(buf).toString('hex'); 86 assert.notStrictEqual(before, after); 87} 88 89{ 90 [ 91 new Uint16Array(10), 92 new Uint32Array(10), 93 new Float32Array(10), 94 new Float64Array(10), 95 new DataView(new ArrayBuffer(10)), 96 ].forEach((buf) => { 97 const before = Buffer.from(buf.buffer).toString('hex'); 98 crypto.randomFillSync(buf); 99 const after = Buffer.from(buf.buffer).toString('hex'); 100 assert.notStrictEqual(before, after); 101 }); 102} 103 104{ 105 const buf = Buffer.alloc(10); 106 const before = buf.toString('hex'); 107 crypto.randomFill(buf, common.mustSucceed((buf) => { 108 const after = buf.toString('hex'); 109 assert.notStrictEqual(before, after); 110 })); 111} 112 113{ 114 const buf = new Uint8Array(new Array(10).fill(0)); 115 const before = Buffer.from(buf).toString('hex'); 116 crypto.randomFill(buf, common.mustSucceed((buf) => { 117 const after = Buffer.from(buf).toString('hex'); 118 assert.notStrictEqual(before, after); 119 })); 120} 121 122{ 123 [ 124 new Uint16Array(10), 125 new Uint32Array(10), 126 new Float32Array(10), 127 new Float64Array(10), 128 new DataView(new ArrayBuffer(10)), 129 ].forEach((buf) => { 130 const before = Buffer.from(buf.buffer).toString('hex'); 131 crypto.randomFill(buf, common.mustSucceed((buf) => { 132 const after = Buffer.from(buf.buffer).toString('hex'); 133 assert.notStrictEqual(before, after); 134 })); 135 }); 136} 137 138{ 139 const buf = Buffer.alloc(10); 140 const before = buf.toString('hex'); 141 crypto.randomFillSync(buf, 5, 5); 142 const after = buf.toString('hex'); 143 assert.notStrictEqual(before, after); 144 assert.deepStrictEqual(before.slice(0, 5), after.slice(0, 5)); 145} 146 147{ 148 const buf = new Uint8Array(new Array(10).fill(0)); 149 const before = Buffer.from(buf).toString('hex'); 150 crypto.randomFillSync(buf, 5, 5); 151 const after = Buffer.from(buf).toString('hex'); 152 assert.notStrictEqual(before, after); 153 assert.deepStrictEqual(before.slice(0, 5), after.slice(0, 5)); 154} 155 156{ 157 const buf = Buffer.alloc(10); 158 const before = buf.toString('hex'); 159 crypto.randomFillSync(buf, 5); 160 const after = buf.toString('hex'); 161 assert.notStrictEqual(before, after); 162 assert.deepStrictEqual(before.slice(0, 5), after.slice(0, 5)); 163} 164 165{ 166 const buf = Buffer.alloc(10); 167 const before = buf.toString('hex'); 168 crypto.randomFill(buf, 5, 5, common.mustSucceed((buf) => { 169 const after = buf.toString('hex'); 170 assert.notStrictEqual(before, after); 171 assert.deepStrictEqual(before.slice(0, 5), after.slice(0, 5)); 172 })); 173} 174 175{ 176 const buf = new Uint8Array(new Array(10).fill(0)); 177 const before = Buffer.from(buf).toString('hex'); 178 crypto.randomFill(buf, 5, 5, common.mustSucceed((buf) => { 179 const after = Buffer.from(buf).toString('hex'); 180 assert.notStrictEqual(before, after); 181 assert.deepStrictEqual(before.slice(0, 5), after.slice(0, 5)); 182 })); 183} 184 185{ 186 [ 187 Buffer.alloc(10), 188 new Uint8Array(new Array(10).fill(0)), 189 ].forEach((buf) => { 190 const len = Buffer.byteLength(buf); 191 assert.strictEqual(len, 10, `Expected byteLength of 10, got ${len}`); 192 193 const typeErrObj = { 194 code: 'ERR_INVALID_ARG_TYPE', 195 name: 'TypeError', 196 message: 'The "offset" argument must be of type number. ' + 197 "Received type string ('test')" 198 }; 199 200 assert.throws(() => crypto.randomFillSync(buf, 'test'), typeErrObj); 201 202 assert.throws( 203 () => crypto.randomFill(buf, 'test', common.mustNotCall()), 204 typeErrObj); 205 206 typeErrObj.message = typeErrObj.message.replace('offset', 'size'); 207 assert.throws(() => crypto.randomFillSync(buf, 0, 'test'), typeErrObj); 208 209 assert.throws( 210 () => crypto.randomFill(buf, 0, 'test', common.mustNotCall()), 211 typeErrObj 212 ); 213 214 [NaN, kMaxPossibleLength + 1, -10, (-1 >>> 0) + 1].forEach((offsetSize) => { 215 const errObj = { 216 code: 'ERR_OUT_OF_RANGE', 217 name: 'RangeError', 218 message: 'The value of "offset" is out of range. ' + 219 `It must be >= 0 && <= 10. Received ${offsetSize}` 220 }; 221 222 assert.throws(() => crypto.randomFillSync(buf, offsetSize), errObj); 223 224 assert.throws( 225 () => crypto.randomFill(buf, offsetSize, common.mustNotCall()), 226 errObj); 227 228 errObj.message = 'The value of "size" is out of range. It must be >= ' + 229 `0 && <= ${kMaxPossibleLength}. Received ${offsetSize}`; 230 assert.throws(() => crypto.randomFillSync(buf, 1, offsetSize), errObj); 231 232 assert.throws( 233 () => crypto.randomFill(buf, 1, offsetSize, common.mustNotCall()), 234 errObj 235 ); 236 }); 237 238 const rangeErrObj = { 239 code: 'ERR_OUT_OF_RANGE', 240 name: 'RangeError', 241 message: 'The value of "size + offset" is out of range. ' + 242 'It must be <= 10. Received 11' 243 }; 244 assert.throws(() => crypto.randomFillSync(buf, 1, 10), rangeErrObj); 245 246 assert.throws( 247 () => crypto.randomFill(buf, 1, 10, common.mustNotCall()), 248 rangeErrObj 249 ); 250 }); 251} 252 253// https://github.com/nodejs/node-v0.x-archive/issues/5126, 254// "FATAL ERROR: v8::Object::SetIndexedPropertiesToExternalArrayData() length 255// exceeds max acceptable value" 256assert.throws( 257 () => crypto.randomBytes((-1 >>> 0) + 1), 258 { 259 code: 'ERR_OUT_OF_RANGE', 260 name: 'RangeError', 261 message: 'The value of "size" is out of range. ' + 262 `It must be >= 0 && <= ${kMaxPossibleLength}. Received 4294967296` 263 } 264); 265 266[1, true, NaN, null, undefined, {}, []].forEach((i) => { 267 const buf = Buffer.alloc(10); 268 assert.throws( 269 () => crypto.randomFillSync(i), 270 { 271 code: 'ERR_INVALID_ARG_TYPE', 272 name: 'TypeError' 273 } 274 ); 275 assert.throws( 276 () => crypto.randomFill(i, common.mustNotCall()), 277 { 278 code: 'ERR_INVALID_ARG_TYPE', 279 name: 'TypeError' 280 } 281 ); 282 assert.throws( 283 () => crypto.randomFill(buf, 0, 10, i), 284 { 285 code: 'ERR_INVALID_CALLBACK', 286 name: 'TypeError', 287 message: `Callback must be a function. Received ${inspect(i)}` 288 }); 289}); 290 291[1, true, NaN, null, {}, []].forEach((i) => { 292 assert.throws( 293 () => crypto.randomBytes(1, i), 294 { 295 code: 'ERR_INVALID_CALLBACK', 296 name: 'TypeError', 297 message: `Callback must be a function. Received ${inspect(i)}` 298 } 299 ); 300}); 301 302 303['pseudoRandomBytes', 'prng', 'rng'].forEach((f) => { 304 const desc = Object.getOwnPropertyDescriptor(crypto, f); 305 assert.ok(desc); 306 assert.strictEqual(desc.configurable, true); 307 assert.strictEqual(desc.writable, true); 308 assert.strictEqual(desc.enumerable, false); 309}); 310 311 312{ 313 // Asynchronous API 314 const randomInts = []; 315 for (let i = 0; i < 100; i++) { 316 crypto.randomInt(3, common.mustSucceed((n) => { 317 assert.ok(n >= 0); 318 assert.ok(n < 3); 319 randomInts.push(n); 320 if (randomInts.length === 100) { 321 assert.ok(!randomInts.includes(-1)); 322 assert.ok(randomInts.includes(0)); 323 assert.ok(randomInts.includes(1)); 324 assert.ok(randomInts.includes(2)); 325 assert.ok(!randomInts.includes(3)); 326 } 327 })); 328 } 329} 330{ 331 // Synchronous API 332 const randomInts = []; 333 for (let i = 0; i < 100; i++) { 334 const n = crypto.randomInt(3); 335 assert.ok(n >= 0); 336 assert.ok(n < 3); 337 randomInts.push(n); 338 } 339 340 assert.ok(!randomInts.includes(-1)); 341 assert.ok(randomInts.includes(0)); 342 assert.ok(randomInts.includes(1)); 343 assert.ok(randomInts.includes(2)); 344 assert.ok(!randomInts.includes(3)); 345} 346{ 347 // Positive range 348 const randomInts = []; 349 for (let i = 0; i < 100; i++) { 350 crypto.randomInt(1, 3, common.mustSucceed((n) => { 351 assert.ok(n >= 1); 352 assert.ok(n < 3); 353 randomInts.push(n); 354 if (randomInts.length === 100) { 355 assert.ok(!randomInts.includes(0)); 356 assert.ok(randomInts.includes(1)); 357 assert.ok(randomInts.includes(2)); 358 assert.ok(!randomInts.includes(3)); 359 } 360 })); 361 } 362} 363{ 364 // Negative range 365 const randomInts = []; 366 for (let i = 0; i < 100; i++) { 367 crypto.randomInt(-10, -8, common.mustSucceed((n) => { 368 assert.ok(n >= -10); 369 assert.ok(n < -8); 370 randomInts.push(n); 371 if (randomInts.length === 100) { 372 assert.ok(!randomInts.includes(-11)); 373 assert.ok(randomInts.includes(-10)); 374 assert.ok(randomInts.includes(-9)); 375 assert.ok(!randomInts.includes(-8)); 376 } 377 })); 378 } 379} 380{ 381 382 ['10', true, NaN, null, {}, []].forEach((i) => { 383 const invalidMinError = { 384 code: 'ERR_INVALID_ARG_TYPE', 385 name: 'TypeError', 386 message: 'The "min" argument must be a safe integer.' + 387 `${common.invalidArgTypeHelper(i)}`, 388 }; 389 const invalidMaxError = { 390 code: 'ERR_INVALID_ARG_TYPE', 391 name: 'TypeError', 392 message: 'The "max" argument must be a safe integer.' + 393 `${common.invalidArgTypeHelper(i)}`, 394 }; 395 396 assert.throws( 397 () => crypto.randomInt(i, 100), 398 invalidMinError 399 ); 400 assert.throws( 401 () => crypto.randomInt(i, 100, common.mustNotCall()), 402 invalidMinError 403 ); 404 assert.throws( 405 () => crypto.randomInt(i), 406 invalidMaxError 407 ); 408 assert.throws( 409 () => crypto.randomInt(i, common.mustNotCall()), 410 invalidMaxError 411 ); 412 assert.throws( 413 () => crypto.randomInt(0, i, common.mustNotCall()), 414 invalidMaxError 415 ); 416 assert.throws( 417 () => crypto.randomInt(0, i), 418 invalidMaxError 419 ); 420 }); 421 422 const maxInt = Number.MAX_SAFE_INTEGER; 423 const minInt = Number.MIN_SAFE_INTEGER; 424 425 crypto.randomInt(minInt, minInt + 5, common.mustSucceed()); 426 crypto.randomInt(maxInt - 5, maxInt, common.mustSucceed()); 427 428 assert.throws( 429 () => crypto.randomInt(minInt - 1, minInt + 5, common.mustNotCall()), 430 { 431 code: 'ERR_INVALID_ARG_TYPE', 432 name: 'TypeError', 433 message: 'The "min" argument must be a safe integer.' + 434 `${common.invalidArgTypeHelper(minInt - 1)}`, 435 } 436 ); 437 438 assert.throws( 439 () => crypto.randomInt(maxInt + 1, common.mustNotCall()), 440 { 441 code: 'ERR_INVALID_ARG_TYPE', 442 name: 'TypeError', 443 message: 'The "max" argument must be a safe integer.' + 444 `${common.invalidArgTypeHelper(maxInt + 1)}`, 445 } 446 ); 447 448 crypto.randomInt(1, common.mustSucceed()); 449 crypto.randomInt(0, 1, common.mustSucceed()); 450 for (const arg of [[0], [1, 1], [3, 2], [-5, -5], [11, -10]]) { 451 assert.throws(() => crypto.randomInt(...arg, common.mustNotCall()), { 452 code: 'ERR_OUT_OF_RANGE', 453 name: 'RangeError', 454 message: 'The value of "max" is out of range. It must be greater than ' + 455 `the value of "min" (${arg[arg.length - 2] || 0}). ` + 456 `Received ${arg[arg.length - 1]}` 457 }); 458 } 459 460 const MAX_RANGE = 0xFFFF_FFFF_FFFF; 461 crypto.randomInt(MAX_RANGE, common.mustSucceed()); 462 crypto.randomInt(1, MAX_RANGE + 1, common.mustSucceed()); 463 assert.throws( 464 () => crypto.randomInt(1, MAX_RANGE + 2, common.mustNotCall()), 465 { 466 code: 'ERR_OUT_OF_RANGE', 467 name: 'RangeError', 468 message: 'The value of "max - min" is out of range. ' + 469 `It must be <= ${MAX_RANGE}. ` + 470 'Received 281_474_976_710_656' 471 } 472 ); 473 474 assert.throws(() => crypto.randomInt(MAX_RANGE + 1, common.mustNotCall()), { 475 code: 'ERR_OUT_OF_RANGE', 476 name: 'RangeError', 477 message: 'The value of "max" is out of range. ' + 478 `It must be <= ${MAX_RANGE}. ` + 479 'Received 281_474_976_710_656' 480 }); 481 482 [true, NaN, null, {}, [], 10].forEach((i) => { 483 const cbError = { 484 code: 'ERR_INVALID_CALLBACK', 485 name: 'TypeError', 486 message: `Callback must be a function. Received ${inspect(i)}` 487 }; 488 assert.throws(() => crypto.randomInt(0, 1, i), cbError); 489 }); 490} 491