Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto: accept decimal Number in randomBytes #15130

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5616,13 +5616,13 @@ void RandomBytesProcessSync(Environment* env,
void RandomBytes(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

if (!args[0]->IsUint32()) {
if (!args[0]->IsNumber() || args[0].As<v8::Number>()->Value() < 0) {
return env->ThrowTypeError("size must be a number >= 0");
}

const int64_t size = args[0]->IntegerValue();
if (size < 0 || size > Buffer::kMaxLength)
return env->ThrowRangeError("size is not a valid Smi");
if (size > Buffer::kMaxLength)
return env->ThrowTypeError("size must be a uint32");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm +1 on this change, but wouldn't feel comfortable with it not being semver-major since it changes an error message

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, why is the RangeError changed to a TypeError?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question: A range error made no sense there - and the code path emitting it didn't happen in reasonable cases anyway. For example if you passed -1.1 you'd get the TypeError in the path above - so not changing this to a TypeError would break a lot more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I am not sure I totally buy the argument of breaking less code here. Since we are changing the error messages here, why not make everything Semantically Correct(r)?

  • -1.1: RangeError: size must be a uint32
  • 240: RangeError: size must be a uint32
  • 'hello': TypeError: size must be a number

Copy link
Member Author

@benjamingr benjamingr Sep 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not saying that a TypeError is more correct here, I'll try to explain what I mean by breakage:

Before both -1.1 and 240 were TypeErrors since ->IsUint32 returns false and this change would make them both RangeErrors.

A RangeError would only happen if the number was a Uint32 and it's smaller than Buffer::kMaxLength which would only happen if the number was bigger than 2147483647 which is never. Only on 32 bit architecture buffers are max sized at 1073741823 which means RangeErrors would only happen on 32 bit and only when the number passed was between 1073741823 and 2147483647 - which I guess is very rare.

I'm fine with breaking this in a semver-major, but I'd rather do that in a different PR titled "make invalid numbers throw RangeError in randomBytes so it can be specifically examined for that change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair 👍


Local<Object> obj = env->randombytes_constructor_template()->
NewInstance(env->context()).ToLocalChecked();
Expand Down
23 changes: 12 additions & 11 deletions test/parallel/test-crypto-random.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ crypto.DEFAULT_ENCODING = 'buffer';
// bump, we register a lot of exit listeners
process.setMaxListeners(256);

const errMessages = {
offsetNotNumber: /^TypeError: offset must be a number$/,
offsetOutOfRange: /^RangeError: offset out of range$/,
offsetNotUInt32: /^TypeError: offset must be a uint32$/,
sizeNotNumber: /^TypeError: size must be a number$/,
sizeNotUInt32: /^TypeError: size must be a uint32$/,
bufferTooSmall: /^RangeError: buffer too small$/,
};

const expectedErrorRegexp = /^TypeError: size must be a number >= 0$/;
[crypto.randomBytes, crypto.pseudoRandomBytes].forEach(function(f) {
[-1, undefined, null, false, true, {}, []].forEach(function(value) {
Expand All @@ -41,10 +50,10 @@ const expectedErrorRegexp = /^TypeError: size must be a number >= 0$/;
expectedErrorRegexp);
});

[0, 1, 2, 4, 16, 256, 1024].forEach(function(len) {
[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, len);
assert.strictEqual(buf.length, Math.floor(len));
assert.ok(Buffer.isBuffer(buf));
}));
});
Expand Down Expand Up @@ -139,14 +148,6 @@ const expectedErrorRegexp = /^TypeError: size must be a number >= 0$/;
Buffer.alloc(10),
new Uint8Array(new Array(10).fill(0))
];
const errMessages = {
offsetNotNumber: /^TypeError: offset must be a number$/,
offsetOutOfRange: /^RangeError: offset out of range$/,
offsetNotUInt32: /^TypeError: offset must be a uint32$/,
sizeNotNumber: /^TypeError: size must be a number$/,
sizeNotUInt32: /^TypeError: size must be a uint32$/,
bufferTooSmall: /^RangeError: buffer too small$/,
};

const max = require('buffer').kMaxLength + 1;

Expand Down Expand Up @@ -264,4 +265,4 @@ const expectedErrorRegexp = /^TypeError: size must be a number >= 0$/;
// length exceeds max acceptable value"
assert.throws(function() {
crypto.randomBytes((-1 >>> 0) + 1);
}, /^TypeError: size must be a number >= 0$/);
}, errMessages.sizeNotUInt32);