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 cryptop = require('crypto').webcrypto; 32const { kMaxLength } = require('buffer'); 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 [ 106 new Uint16Array(10), 107 new Uint32Array(10), 108 ].forEach((buf) => { 109 const before = Buffer.from(buf.buffer).toString('hex'); 110 cryptop.getRandomValues(buf); 111 const after = Buffer.from(buf.buffer).toString('hex'); 112 assert.notStrictEqual(before, after); 113 }); 114} 115 116{ 117 [ 118 new ArrayBuffer(10), 119 new SharedArrayBuffer(10), 120 ].forEach((buf) => { 121 const before = Buffer.from(buf).toString('hex'); 122 crypto.randomFillSync(buf); 123 const after = Buffer.from(buf).toString('hex'); 124 assert.notStrictEqual(before, after); 125 }); 126} 127 128{ 129 const buf = Buffer.alloc(10); 130 const before = buf.toString('hex'); 131 crypto.randomFill(buf, common.mustSucceed((buf) => { 132 const after = buf.toString('hex'); 133 assert.notStrictEqual(before, after); 134 })); 135} 136 137{ 138 const buf = new Uint8Array(new Array(10).fill(0)); 139 const before = Buffer.from(buf).toString('hex'); 140 crypto.randomFill(buf, common.mustSucceed((buf) => { 141 const after = Buffer.from(buf).toString('hex'); 142 assert.notStrictEqual(before, after); 143 })); 144} 145 146{ 147 [ 148 new Uint16Array(10), 149 new Uint32Array(10), 150 new Float32Array(10), 151 new Float64Array(10), 152 new DataView(new ArrayBuffer(10)), 153 ].forEach((buf) => { 154 const before = Buffer.from(buf.buffer).toString('hex'); 155 crypto.randomFill(buf, common.mustSucceed((buf) => { 156 const after = Buffer.from(buf.buffer).toString('hex'); 157 assert.notStrictEqual(before, after); 158 })); 159 }); 160} 161 162{ 163 [ 164 new ArrayBuffer(10), 165 new SharedArrayBuffer(10), 166 ].forEach((buf) => { 167 const before = Buffer.from(buf).toString('hex'); 168 crypto.randomFill(buf, common.mustSucceed((buf) => { 169 const after = Buffer.from(buf).toString('hex'); 170 assert.notStrictEqual(before, after); 171 })); 172 }); 173} 174 175{ 176 const buf = Buffer.alloc(10); 177 const before = buf.toString('hex'); 178 crypto.randomFillSync(buf, 5, 5); 179 const after = buf.toString('hex'); 180 assert.notStrictEqual(before, after); 181 assert.deepStrictEqual(before.slice(0, 5), after.slice(0, 5)); 182} 183 184{ 185 const buf = new Uint8Array(new Array(10).fill(0)); 186 const before = Buffer.from(buf).toString('hex'); 187 crypto.randomFillSync(buf, 5, 5); 188 const after = Buffer.from(buf).toString('hex'); 189 assert.notStrictEqual(before, after); 190 assert.deepStrictEqual(before.slice(0, 5), after.slice(0, 5)); 191} 192 193{ 194 const buf = Buffer.alloc(10); 195 const before = buf.toString('hex'); 196 crypto.randomFillSync(buf, 5); 197 const after = buf.toString('hex'); 198 assert.notStrictEqual(before, after); 199 assert.deepStrictEqual(before.slice(0, 5), after.slice(0, 5)); 200} 201 202{ 203 const buf = Buffer.alloc(10); 204 const before = buf.toString('hex'); 205 crypto.randomFill(buf, 5, 5, common.mustSucceed((buf) => { 206 const after = buf.toString('hex'); 207 assert.notStrictEqual(before, after); 208 assert.deepStrictEqual(before.slice(0, 5), after.slice(0, 5)); 209 })); 210} 211 212{ 213 const buf = new Uint8Array(new Array(10).fill(0)); 214 const before = Buffer.from(buf).toString('hex'); 215 crypto.randomFill(buf, 5, 5, common.mustSucceed((buf) => { 216 const after = Buffer.from(buf).toString('hex'); 217 assert.notStrictEqual(before, after); 218 assert.deepStrictEqual(before.slice(0, 5), after.slice(0, 5)); 219 })); 220} 221 222{ 223 [ 224 Buffer.alloc(10), 225 new Uint8Array(new Array(10).fill(0)), 226 ].forEach((buf) => { 227 const len = Buffer.byteLength(buf); 228 assert.strictEqual(len, 10, `Expected byteLength of 10, got ${len}`); 229 230 const typeErrObj = { 231 code: 'ERR_INVALID_ARG_TYPE', 232 name: 'TypeError', 233 message: 'The "offset" argument must be of type number. ' + 234 "Received type string ('test')" 235 }; 236 237 assert.throws(() => crypto.randomFillSync(buf, 'test'), typeErrObj); 238 239 assert.throws( 240 () => crypto.randomFill(buf, 'test', common.mustNotCall()), 241 typeErrObj); 242 243 typeErrObj.message = typeErrObj.message.replace('offset', 'size'); 244 assert.throws(() => crypto.randomFillSync(buf, 0, 'test'), typeErrObj); 245 246 assert.throws( 247 () => crypto.randomFill(buf, 0, 'test', common.mustNotCall()), 248 typeErrObj 249 ); 250 251 [NaN, kMaxPossibleLength + 1, -10, (-1 >>> 0) + 1].forEach((offsetSize) => { 252 const errObj = { 253 code: 'ERR_OUT_OF_RANGE', 254 name: 'RangeError', 255 message: 'The value of "offset" is out of range. ' + 256 `It must be >= 0 && <= 10. Received ${offsetSize}` 257 }; 258 259 assert.throws(() => crypto.randomFillSync(buf, offsetSize), errObj); 260 261 assert.throws( 262 () => crypto.randomFill(buf, offsetSize, common.mustNotCall()), 263 errObj); 264 265 errObj.message = 'The value of "size" is out of range. It must be >= ' + 266 `0 && <= ${kMaxPossibleLength}. Received ${offsetSize}`; 267 assert.throws(() => crypto.randomFillSync(buf, 1, offsetSize), errObj); 268 269 assert.throws( 270 () => crypto.randomFill(buf, 1, offsetSize, common.mustNotCall()), 271 errObj 272 ); 273 }); 274 275 const rangeErrObj = { 276 code: 'ERR_OUT_OF_RANGE', 277 name: 'RangeError', 278 message: 'The value of "size + offset" is out of range. ' + 279 'It must be <= 10. Received 11' 280 }; 281 assert.throws(() => crypto.randomFillSync(buf, 1, 10), rangeErrObj); 282 283 assert.throws( 284 () => crypto.randomFill(buf, 1, 10, common.mustNotCall()), 285 rangeErrObj 286 ); 287 }); 288} 289 290// https://github.com/nodejs/node-v0.x-archive/issues/5126, 291// "FATAL ERROR: v8::Object::SetIndexedPropertiesToExternalArrayData() length 292// exceeds max acceptable value" 293assert.throws( 294 () => crypto.randomBytes((-1 >>> 0) + 1), 295 { 296 code: 'ERR_OUT_OF_RANGE', 297 name: 'RangeError', 298 message: 'The value of "size" is out of range. ' + 299 `It must be >= 0 && <= ${kMaxPossibleLength}. Received 4294967296` 300 } 301); 302 303[1, true, NaN, null, undefined, {}, []].forEach((i) => { 304 const buf = Buffer.alloc(10); 305 assert.throws( 306 () => crypto.randomFillSync(i), 307 { 308 code: 'ERR_INVALID_ARG_TYPE', 309 name: 'TypeError' 310 } 311 ); 312 assert.throws( 313 () => crypto.randomFill(i, common.mustNotCall()), 314 { 315 code: 'ERR_INVALID_ARG_TYPE', 316 name: 'TypeError' 317 } 318 ); 319 assert.throws( 320 () => crypto.randomFill(buf, 0, 10, i), 321 { 322 code: 'ERR_INVALID_ARG_TYPE', 323 name: 'TypeError', 324 }); 325}); 326 327[1, true, NaN, null, {}, []].forEach((i) => { 328 assert.throws( 329 () => crypto.randomBytes(1, i), 330 { 331 code: 'ERR_INVALID_ARG_TYPE', 332 name: 'TypeError', 333 } 334 ); 335}); 336 337['pseudoRandomBytes', 'prng', 'rng'].forEach((f) => { 338 const desc = Object.getOwnPropertyDescriptor(crypto, f); 339 assert.ok(desc); 340 assert.strictEqual(desc.configurable, true); 341 assert.strictEqual(desc.enumerable, false); 342}); 343 344 345{ 346 // Asynchronous API 347 const randomInts = []; 348 for (let i = 0; i < 100; i++) { 349 crypto.randomInt(3, common.mustSucceed((n) => { 350 assert.ok(n >= 0); 351 assert.ok(n < 3); 352 randomInts.push(n); 353 if (randomInts.length === 100) { 354 assert.ok(!randomInts.includes(-1)); 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 // Synchronous API 365 const randomInts = []; 366 for (let i = 0; i < 100; i++) { 367 const n = crypto.randomInt(3); 368 assert.ok(n >= 0); 369 assert.ok(n < 3); 370 randomInts.push(n); 371 } 372 373 assert.ok(!randomInts.includes(-1)); 374 assert.ok(randomInts.includes(0)); 375 assert.ok(randomInts.includes(1)); 376 assert.ok(randomInts.includes(2)); 377 assert.ok(!randomInts.includes(3)); 378} 379{ 380 // Positive range 381 const randomInts = []; 382 for (let i = 0; i < 100; i++) { 383 crypto.randomInt(1, 3, common.mustSucceed((n) => { 384 assert.ok(n >= 1); 385 assert.ok(n < 3); 386 randomInts.push(n); 387 if (randomInts.length === 100) { 388 assert.ok(!randomInts.includes(0)); 389 assert.ok(randomInts.includes(1)); 390 assert.ok(randomInts.includes(2)); 391 assert.ok(!randomInts.includes(3)); 392 } 393 })); 394 } 395} 396{ 397 // Negative range 398 const randomInts = []; 399 for (let i = 0; i < 100; i++) { 400 crypto.randomInt(-10, -8, common.mustSucceed((n) => { 401 assert.ok(n >= -10); 402 assert.ok(n < -8); 403 randomInts.push(n); 404 if (randomInts.length === 100) { 405 assert.ok(!randomInts.includes(-11)); 406 assert.ok(randomInts.includes(-10)); 407 assert.ok(randomInts.includes(-9)); 408 assert.ok(!randomInts.includes(-8)); 409 } 410 })); 411 } 412} 413{ 414 415 ['10', true, NaN, null, {}, []].forEach((i) => { 416 const invalidMinError = { 417 code: 'ERR_INVALID_ARG_TYPE', 418 name: 'TypeError', 419 message: 'The "min" argument must be a safe integer.' + 420 `${common.invalidArgTypeHelper(i)}`, 421 }; 422 const invalidMaxError = { 423 code: 'ERR_INVALID_ARG_TYPE', 424 name: 'TypeError', 425 message: 'The "max" argument must be a safe integer.' + 426 `${common.invalidArgTypeHelper(i)}`, 427 }; 428 429 assert.throws( 430 () => crypto.randomInt(i, 100), 431 invalidMinError 432 ); 433 assert.throws( 434 () => crypto.randomInt(i, 100, common.mustNotCall()), 435 invalidMinError 436 ); 437 assert.throws( 438 () => crypto.randomInt(i), 439 invalidMaxError 440 ); 441 assert.throws( 442 () => crypto.randomInt(i, common.mustNotCall()), 443 invalidMaxError 444 ); 445 assert.throws( 446 () => crypto.randomInt(0, i, common.mustNotCall()), 447 invalidMaxError 448 ); 449 assert.throws( 450 () => crypto.randomInt(0, i), 451 invalidMaxError 452 ); 453 }); 454 455 const maxInt = Number.MAX_SAFE_INTEGER; 456 const minInt = Number.MIN_SAFE_INTEGER; 457 458 crypto.randomInt(minInt, minInt + 5, common.mustSucceed()); 459 crypto.randomInt(maxInt - 5, maxInt, common.mustSucceed()); 460 461 assert.throws( 462 () => crypto.randomInt(minInt - 1, minInt + 5, common.mustNotCall()), 463 { 464 code: 'ERR_INVALID_ARG_TYPE', 465 name: 'TypeError', 466 message: 'The "min" argument must be a safe integer.' + 467 `${common.invalidArgTypeHelper(minInt - 1)}`, 468 } 469 ); 470 471 assert.throws( 472 () => crypto.randomInt(maxInt + 1, common.mustNotCall()), 473 { 474 code: 'ERR_INVALID_ARG_TYPE', 475 name: 'TypeError', 476 message: 'The "max" argument must be a safe integer.' + 477 `${common.invalidArgTypeHelper(maxInt + 1)}`, 478 } 479 ); 480 481 crypto.randomInt(1, common.mustSucceed()); 482 crypto.randomInt(0, 1, common.mustSucceed()); 483 for (const arg of [[0], [1, 1], [3, 2], [-5, -5], [11, -10]]) { 484 assert.throws(() => crypto.randomInt(...arg, common.mustNotCall()), { 485 code: 'ERR_OUT_OF_RANGE', 486 name: 'RangeError', 487 message: 'The value of "max" is out of range. It must be greater than ' + 488 `the value of "min" (${arg[arg.length - 2] || 0}). ` + 489 `Received ${arg[arg.length - 1]}` 490 }); 491 } 492 493 const MAX_RANGE = 0xFFFF_FFFF_FFFF; 494 crypto.randomInt(MAX_RANGE, common.mustSucceed()); 495 crypto.randomInt(1, MAX_RANGE + 1, common.mustSucceed()); 496 assert.throws( 497 () => crypto.randomInt(1, MAX_RANGE + 2, common.mustNotCall()), 498 { 499 code: 'ERR_OUT_OF_RANGE', 500 name: 'RangeError', 501 message: 'The value of "max - min" is out of range. ' + 502 `It must be <= ${MAX_RANGE}. ` + 503 'Received 281_474_976_710_656' 504 } 505 ); 506 507 assert.throws(() => crypto.randomInt(MAX_RANGE + 1, common.mustNotCall()), { 508 code: 'ERR_OUT_OF_RANGE', 509 name: 'RangeError', 510 message: 'The value of "max" is out of range. ' + 511 `It must be <= ${MAX_RANGE}. ` + 512 'Received 281_474_976_710_656' 513 }); 514 515 [true, NaN, null, {}, [], 10].forEach((i) => { 516 const cbError = { 517 code: 'ERR_INVALID_ARG_TYPE', 518 name: 'TypeError', 519 }; 520 assert.throws(() => crypto.randomInt(0, 1, i), cbError); 521 }); 522} 523 524{ 525 // Verify that it doesn't throw or abort 526 crypto.randomFill(new Uint16Array(10), 0, common.mustSucceed()); 527 crypto.randomFill(new Uint32Array(10), 0, common.mustSucceed()); 528 crypto.randomFill(new Uint32Array(10), 0, 1, common.mustSucceed()); 529} 530