crypto: fix error handling
This fixes multiple cases where the wrong error was returned in case of e.g. a overflow / wrong type. PR-URL: https://github.com/nodejs/node/pull/19445 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
parent
c1278e5329
commit
333adf61eb
@ -13,37 +13,44 @@ const {
|
||||
|
||||
const { kMaxLength } = require('buffer');
|
||||
const kMaxUint32 = Math.pow(2, 32) - 1;
|
||||
const kMaxPossibleLength = Math.min(kMaxLength, kMaxUint32);
|
||||
|
||||
function assertOffset(offset, length) {
|
||||
if (typeof offset !== 'number' || Number.isNaN(offset)) {
|
||||
throw new ERR_INVALID_ARG_TYPE('offset', 'number');
|
||||
function assertOffset(offset, elementSize, length) {
|
||||
if (typeof offset !== 'number') {
|
||||
throw new ERR_INVALID_ARG_TYPE('offset', 'number', offset);
|
||||
}
|
||||
|
||||
if (offset > kMaxUint32 || offset < 0) {
|
||||
throw new ERR_INVALID_ARG_TYPE('offset', 'uint32');
|
||||
offset *= elementSize;
|
||||
|
||||
const maxLength = Math.min(length, kMaxPossibleLength);
|
||||
if (Number.isNaN(offset) || offset > maxLength || offset < 0) {
|
||||
throw new ERR_OUT_OF_RANGE('offset', `>= 0 && <= ${maxLength}`, offset);
|
||||
}
|
||||
|
||||
if (offset > kMaxLength || offset > length) {
|
||||
throw new ERR_OUT_OF_RANGE('offset');
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
function assertSize(size, offset = 0, length = Infinity) {
|
||||
if (typeof size !== 'number' || Number.isNaN(size)) {
|
||||
throw new ERR_INVALID_ARG_TYPE('size', 'number');
|
||||
function assertSize(size, elementSize, offset, length) {
|
||||
if (typeof size !== 'number') {
|
||||
throw new ERR_INVALID_ARG_TYPE('size', 'number', size);
|
||||
}
|
||||
|
||||
if (size > kMaxUint32 || size < 0) {
|
||||
throw new ERR_INVALID_ARG_TYPE('size', 'uint32');
|
||||
size *= elementSize;
|
||||
|
||||
if (Number.isNaN(size) || size > kMaxPossibleLength || size < 0) {
|
||||
throw new ERR_OUT_OF_RANGE('size',
|
||||
`>= 0 && <= ${kMaxPossibleLength}`, size);
|
||||
}
|
||||
|
||||
if (size + offset > length || size > kMaxLength) {
|
||||
throw new ERR_OUT_OF_RANGE('size');
|
||||
if (size + offset > length) {
|
||||
throw new ERR_OUT_OF_RANGE('size + offset', `<= ${length}`, size + offset);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
function randomBytes(size, cb) {
|
||||
assertSize(size);
|
||||
assertSize(size, 1, 0, Infinity);
|
||||
if (cb !== undefined && typeof cb !== 'function')
|
||||
throw new ERR_INVALID_CALLBACK();
|
||||
return _randomBytes(size, cb);
|
||||
@ -56,17 +63,14 @@ function randomFillSync(buf, offset = 0, size) {
|
||||
|
||||
const elementSize = buf.BYTES_PER_ELEMENT || 1;
|
||||
|
||||
offset *= elementSize;
|
||||
assertOffset(offset, buf.byteLength);
|
||||
offset = assertOffset(offset, elementSize, buf.byteLength);
|
||||
|
||||
if (size === undefined) {
|
||||
size = buf.byteLength - offset;
|
||||
} else {
|
||||
size *= elementSize;
|
||||
size = assertSize(size, elementSize, offset, buf.byteLength);
|
||||
}
|
||||
|
||||
assertSize(size, offset, buf.byteLength);
|
||||
|
||||
return _randomFill(buf, offset, size);
|
||||
}
|
||||
|
||||
@ -83,20 +87,19 @@ function randomFill(buf, offset, size, cb) {
|
||||
size = buf.bytesLength;
|
||||
} else if (typeof size === 'function') {
|
||||
cb = size;
|
||||
offset *= elementSize;
|
||||
size = buf.byteLength - offset;
|
||||
} else if (typeof cb !== 'function') {
|
||||
throw new ERR_INVALID_CALLBACK();
|
||||
}
|
||||
|
||||
offset = assertOffset(offset, elementSize, buf.byteLength);
|
||||
|
||||
if (size === undefined) {
|
||||
size = buf.byteLength - offset;
|
||||
} else {
|
||||
size *= elementSize;
|
||||
size = assertSize(size, elementSize, offset, buf.byteLength);
|
||||
}
|
||||
|
||||
assertOffset(offset, buf.byteLength);
|
||||
assertSize(size, offset, buf.byteLength);
|
||||
|
||||
return _randomFill(buf, offset, size, cb);
|
||||
}
|
||||
|
||||
|
@ -27,42 +27,49 @@ if (!common.hasCrypto)
|
||||
|
||||
const assert = require('assert');
|
||||
const crypto = require('crypto');
|
||||
const { kMaxLength } = require('buffer');
|
||||
|
||||
const kMaxUint32 = Math.pow(2, 32) - 1;
|
||||
const kMaxPossibleLength = Math.min(kMaxLength, kMaxUint32);
|
||||
|
||||
crypto.DEFAULT_ENCODING = 'buffer';
|
||||
|
||||
// bump, we register a lot of exit listeners
|
||||
process.setMaxListeners(256);
|
||||
|
||||
[crypto.randomBytes, crypto.pseudoRandomBytes].forEach(function(f) {
|
||||
[-1, undefined, null, false, true, {}, []].forEach(function(value) {
|
||||
|
||||
common.expectsError(
|
||||
() => f(value),
|
||||
{
|
||||
{
|
||||
[crypto.randomBytes, crypto.pseudoRandomBytes].forEach((f) => {
|
||||
[undefined, null, false, true, {}, []].forEach((value) => {
|
||||
const errObj = {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: /^The "size" argument must be of type (number|uint32)$/
|
||||
}
|
||||
);
|
||||
name: 'TypeError [ERR_INVALID_ARG_TYPE]',
|
||||
message: 'The "size" argument must be of type number. ' +
|
||||
`Received type ${typeof value}`
|
||||
};
|
||||
assert.throws(() => f(value), errObj);
|
||||
assert.throws(() => f(value, common.mustNotCall()), errObj);
|
||||
});
|
||||
|
||||
common.expectsError(
|
||||
() => f(value, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: /^The "size" argument must be of type (number|uint32)$/
|
||||
}
|
||||
);
|
||||
});
|
||||
[-1, NaN, 2 ** 32].forEach((value) => {
|
||||
const errObj = {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError [ERR_OUT_OF_RANGE]',
|
||||
message: 'The value of "size" is out of range. It must be >= 0 && <= ' +
|
||||
`${kMaxPossibleLength}. Received ${value}`
|
||||
};
|
||||
assert.throws(() => f(value), errObj);
|
||||
assert.throws(() => f(value, common.mustNotCall()), errObj);
|
||||
});
|
||||
|
||||
[0, 1, 2, 4, 16, 256, 1024, 101.2].forEach(function(len) {
|
||||
f(len, common.mustCall(function(ex, buf) {
|
||||
assert.strictEqual(ex, null);
|
||||
assert.strictEqual(buf.length, Math.floor(len));
|
||||
assert.ok(Buffer.isBuffer(buf));
|
||||
}));
|
||||
[0, 1, 2, 4, 16, 256, 1024, 101.2].forEach((len) => {
|
||||
f(len, common.mustCall((ex, buf) => {
|
||||
assert.strictEqual(ex, null);
|
||||
assert.strictEqual(buf.length, Math.floor(len));
|
||||
assert.ok(Buffer.isBuffer(buf));
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const buf = Buffer.alloc(10);
|
||||
@ -181,252 +188,84 @@ process.setMaxListeners(256);
|
||||
}
|
||||
|
||||
{
|
||||
const bufs = [
|
||||
[
|
||||
Buffer.alloc(10),
|
||||
new Uint8Array(new Array(10).fill(0))
|
||||
];
|
||||
|
||||
const max = require('buffer').kMaxLength + 1;
|
||||
|
||||
for (const buf of bufs) {
|
||||
].forEach((buf) => {
|
||||
const len = Buffer.byteLength(buf);
|
||||
assert.strictEqual(len, 10, `Expected byteLength of 10, got ${len}`);
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFillSync(buf, 'test'),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "offset" argument must be of type number'
|
||||
}
|
||||
);
|
||||
const typeErrObj = {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError [ERR_INVALID_ARG_TYPE]',
|
||||
message: 'The "offset" argument must be of type number. ' +
|
||||
'Received type string'
|
||||
};
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFillSync(buf, NaN),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "offset" argument must be of type number'
|
||||
}
|
||||
);
|
||||
assert.throws(() => crypto.randomFillSync(buf, 'test'), typeErrObj);
|
||||
|
||||
common.expectsError(
|
||||
assert.throws(
|
||||
() => crypto.randomFill(buf, 'test', common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "offset" argument must be of type number'
|
||||
}
|
||||
);
|
||||
typeErrObj);
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFill(buf, NaN, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "offset" argument must be of type number'
|
||||
}
|
||||
);
|
||||
typeErrObj.message = 'The "size" argument must be of type number. ' +
|
||||
'Received type string';
|
||||
assert.throws(() => crypto.randomFillSync(buf, 0, 'test'), typeErrObj);
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFillSync(buf, 11),
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
type: RangeError,
|
||||
message: 'The value of "offset" is out of range.'
|
||||
}
|
||||
);
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFillSync(buf, max),
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
type: RangeError,
|
||||
message: 'The value of "offset" is out of range.'
|
||||
}
|
||||
);
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFill(buf, 11, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
type: RangeError,
|
||||
message: 'The value of "offset" is out of range.'
|
||||
}
|
||||
);
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFill(buf, max, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
type: RangeError,
|
||||
message: 'The value of "offset" is out of range.'
|
||||
}
|
||||
);
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFillSync(buf, 0, 'test'),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "size" argument must be of type number'
|
||||
}
|
||||
);
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFillSync(buf, 0, NaN),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "size" argument must be of type number'
|
||||
}
|
||||
);
|
||||
|
||||
common.expectsError(
|
||||
assert.throws(
|
||||
() => crypto.randomFill(buf, 0, 'test', common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "size" argument must be of type number'
|
||||
}
|
||||
typeErrObj
|
||||
);
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFill(buf, 0, NaN, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "size" argument must be of type number'
|
||||
}
|
||||
);
|
||||
|
||||
{
|
||||
const size = (-1 >>> 0) + 1;
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFillSync(buf, 0, -10),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "size" argument must be of type uint32'
|
||||
}
|
||||
);
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFillSync(buf, 0, size),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "size" argument must be of type uint32'
|
||||
}
|
||||
);
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFill(buf, 0, -10, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "size" argument must be of type uint32'
|
||||
}
|
||||
);
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFill(buf, 0, size, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "size" argument must be of type uint32'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFillSync(buf, -10),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "offset" argument must be of type uint32'
|
||||
}
|
||||
);
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFill(buf, -10, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "offset" argument must be of type uint32'
|
||||
}
|
||||
);
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFillSync(buf, 1, 10),
|
||||
{
|
||||
[NaN, kMaxPossibleLength + 1, -10, (-1 >>> 0) + 1].forEach((offsetSize) => {
|
||||
const errObj = {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
type: RangeError,
|
||||
message: 'The value of "size" is out of range.'
|
||||
}
|
||||
);
|
||||
name: 'RangeError [ERR_OUT_OF_RANGE]',
|
||||
message: 'The value of "offset" is out of range. ' +
|
||||
`It must be >= 0 && <= 10. Received ${offsetSize}`
|
||||
};
|
||||
|
||||
common.expectsError(
|
||||
assert.throws(() => crypto.randomFillSync(buf, offsetSize), errObj);
|
||||
|
||||
assert.throws(
|
||||
() => crypto.randomFill(buf, offsetSize, common.mustNotCall()),
|
||||
errObj);
|
||||
|
||||
errObj.message = 'The value of "size" is out of range. It must be >= ' +
|
||||
`0 && <= ${kMaxPossibleLength}. Received ${offsetSize}`;
|
||||
assert.throws(() => crypto.randomFillSync(buf, 1, offsetSize), errObj);
|
||||
|
||||
assert.throws(
|
||||
() => crypto.randomFill(buf, 1, offsetSize, common.mustNotCall()),
|
||||
errObj
|
||||
);
|
||||
});
|
||||
|
||||
const rangeErrObj = {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError [ERR_OUT_OF_RANGE]',
|
||||
message: 'The value of "size + offset" is out of range. ' +
|
||||
'It must be <= 10. Received 11'
|
||||
};
|
||||
assert.throws(() => crypto.randomFillSync(buf, 1, 10), rangeErrObj);
|
||||
|
||||
assert.throws(
|
||||
() => crypto.randomFill(buf, 1, 10, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
type: RangeError,
|
||||
message: 'The value of "size" is out of range.'
|
||||
}
|
||||
rangeErrObj
|
||||
);
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFillSync(buf, 0, 12),
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
type: RangeError,
|
||||
message: 'The value of "size" is out of range.'
|
||||
}
|
||||
);
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFill(buf, 0, 12, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
type: RangeError,
|
||||
message: 'The value of "size" is out of range.'
|
||||
}
|
||||
);
|
||||
|
||||
{
|
||||
// Offset is too big
|
||||
const offset = (-1 >>> 0) + 1;
|
||||
common.expectsError(
|
||||
() => crypto.randomFillSync(buf, offset, 10),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "offset" argument must be of type uint32'
|
||||
}
|
||||
);
|
||||
|
||||
common.expectsError(
|
||||
() => crypto.randomFill(buf, offset, 10, common.mustNotCall()),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "offset" argument must be of type uint32'
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// https://github.com/nodejs/node-v0.x-archive/issues/5126,
|
||||
// "FATAL ERROR: v8::Object::SetIndexedPropertiesToExternalArrayData() length
|
||||
// exceeds max acceptable value"
|
||||
common.expectsError(
|
||||
assert.throws(
|
||||
() => crypto.randomBytes((-1 >>> 0) + 1),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "size" argument must be of type uint32'
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
name: 'RangeError [ERR_OUT_OF_RANGE]',
|
||||
message: 'The value of "size" is out of range. ' +
|
||||
`It must be >= 0 && <= ${kMaxPossibleLength}. Received 4294967296`
|
||||
}
|
||||
);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user