Skip to content

Commit eeada6c

Browse files
committed
crypto: migrate timingSafeEqual to internal/errors
PR-URL: #16448 Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Joyee Cheung <[email protected]>
1 parent 6645126 commit eeada6c

File tree

6 files changed

+60
-21
lines changed

6 files changed

+60
-21
lines changed

doc/api/errors.md

+7
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,12 @@ Used when an invalid crypto engine identifier is passed to
648648

649649
Used when an invalid [crypto digest algorithm][] is specified.
650650

651+
<a id="ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH"></a>
652+
### ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH
653+
654+
Used when calling [`crypto.timingSafeEqual()`][] with `Buffer`, `TypedArray`,
655+
or `DataView` arguments of different lengths.
656+
651657
<a id="ERR_DNS_SET_SERVERS_FAILED"></a>
652658
### ERR_DNS_SET_SERVERS_FAILED
653659

@@ -1348,6 +1354,7 @@ Used when a given value is out of the accepted range.
13481354
Used when an attempt is made to use a `zlib` object after it has already been
13491355
closed.
13501356

1357+
[`crypto.timingSafeEqual()`]: crypto.html#crypto_crypto_timingsafeequal_a_b
13511358
[`ERR_INVALID_ARG_TYPE`]: #ERR_INVALID_ARG_TYPE
13521359
[`subprocess.kill()`]: child_process.html#child_process_subprocess_kill_signal
13531360
[`subprocess.send()`]: child_process.html#child_process_subprocess_send_message_sendhandle_options_callback

lib/crypto.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ const constants = process.binding('constants').crypto;
3434
const {
3535
getFipsCrypto,
3636
setFipsCrypto,
37-
timingSafeEqual
3837
} = process.binding('crypto');
3938
const {
4039
randomBytes,
@@ -75,6 +74,7 @@ const {
7574
getHashes,
7675
setDefaultEncoding,
7776
setEngine,
77+
timingSafeEqual,
7878
toBuf
7979
} = require('internal/crypto/util');
8080
const Certificate = require('internal/crypto/certificate');

lib/internal/crypto/util.js

+21-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ const {
44
getCiphers: _getCiphers,
55
getCurves: _getCurves,
66
getHashes: _getHashes,
7-
setEngine: _setEngine
7+
setEngine: _setEngine,
8+
timingSafeEqual: _timingSafeEqual
89
} = process.binding('crypto');
910

1011
const {
@@ -17,6 +18,9 @@ const {
1718
cachedResult,
1819
filterDuplicateStrings
1920
} = require('internal/util');
21+
const {
22+
isArrayBufferView
23+
} = require('internal/util/types');
2024

2125
var defaultEncoding = 'buffer';
2226

@@ -60,12 +64,28 @@ function setEngine(id, flags) {
6064
throw new errors.Error('ERR_CRYPTO_ENGINE_UNKNOWN', id);
6165
}
6266

67+
function timingSafeEqual(a, b) {
68+
if (!isArrayBufferView(a)) {
69+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'a',
70+
['Buffer', 'TypedArray', 'DataView']);
71+
}
72+
if (!isArrayBufferView(b)) {
73+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'b',
74+
['Buffer', 'TypedArray', 'DataView']);
75+
}
76+
if (a.length !== b.length) {
77+
throw new errors.RangeError('ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH');
78+
}
79+
return _timingSafeEqual(a, b);
80+
}
81+
6382
module.exports = {
6483
getCiphers,
6584
getCurves,
6685
getDefaultEncoding,
6786
getHashes,
6887
setDefaultEncoding,
6988
setEngine,
89+
timingSafeEqual,
7090
toBuf
7191
};

lib/internal/errors.js

+2
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ E('ERR_CRYPTO_HASH_FINALIZED', 'Digest already called');
160160
E('ERR_CRYPTO_HASH_UPDATE_FAILED', 'Hash update failed');
161161
E('ERR_CRYPTO_INVALID_DIGEST', 'Invalid digest: %s');
162162
E('ERR_CRYPTO_SIGN_KEY_REQUIRED', 'No key provided to sign');
163+
E('ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH',
164+
'Input buffers must have the same length');
163165
E('ERR_DNS_SET_SERVERS_FAILED', (err, servers) =>
164166
`c-ares failed to set servers: "${err}" [${servers}]`);
165167
E('ERR_ENCODING_INVALID_ENCODED_DATA',

src/node_crypto.cc

+3-7
Original file line numberDiff line numberDiff line change
@@ -5848,15 +5848,11 @@ void ExportChallenge(const FunctionCallbackInfo<Value>& args) {
58485848
}
58495849

58505850
void TimingSafeEqual(const FunctionCallbackInfo<Value>& args) {
5851-
Environment* env = Environment::GetCurrent(args);
5852-
5853-
THROW_AND_RETURN_IF_NOT_BUFFER(args[0], "First argument");
5854-
THROW_AND_RETURN_IF_NOT_BUFFER(args[1], "Second argument");
5851+
CHECK(Buffer::HasInstance(args[0]));
5852+
CHECK(Buffer::HasInstance(args[1]));
58555853

58565854
size_t buf_length = Buffer::Length(args[0]);
5857-
if (buf_length != Buffer::Length(args[1])) {
5858-
return env->ThrowTypeError("Input buffers must have the same length");
5859-
}
5855+
CHECK_EQ(buf_length, Buffer::Length(args[1]));
58605856

58615857
const char* buf1 = Buffer::Data(args[0]);
58625858
const char* buf2 = Buffer::Data(args[1]);

test/sequential/test-crypto-timing-safe-equal.js

+26-12
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,31 @@ assert.strictEqual(
1818
'should consider unequal strings to be unequal'
1919
);
2020

21-
assert.throws(function() {
22-
crypto.timingSafeEqual(Buffer.from([1, 2, 3]), Buffer.from([1, 2]));
23-
}, /^TypeError: Input buffers must have the same length$/,
24-
'should throw when given buffers with different lengths');
21+
common.expectsError(
22+
() => crypto.timingSafeEqual(Buffer.from([1, 2, 3]), Buffer.from([1, 2])),
23+
{
24+
code: 'ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH',
25+
type: RangeError,
26+
message: 'Input buffers must have the same length'
27+
}
28+
);
2529

26-
assert.throws(function() {
27-
crypto.timingSafeEqual('not a buffer', Buffer.from([1, 2]));
28-
}, /^TypeError: First argument must be a buffer$/,
29-
'should throw if the first argument is not a buffer');
30+
common.expectsError(
31+
() => crypto.timingSafeEqual('not a buffer', Buffer.from([1, 2])),
32+
{
33+
code: 'ERR_INVALID_ARG_TYPE',
34+
type: TypeError,
35+
message:
36+
'The "a" argument must be one of type Buffer, TypedArray, or DataView'
37+
}
38+
);
3039

31-
assert.throws(function() {
32-
crypto.timingSafeEqual(Buffer.from([1, 2]), 'not a buffer');
33-
}, /^TypeError: Second argument must be a buffer$/,
34-
'should throw if the second argument is not a buffer');
40+
common.expectsError(
41+
() => crypto.timingSafeEqual(Buffer.from([1, 2]), 'not a buffer'),
42+
{
43+
code: 'ERR_INVALID_ARG_TYPE',
44+
type: TypeError,
45+
message:
46+
'The "b" argument must be one of type Buffer, TypedArray, or DataView'
47+
}
48+
);

0 commit comments

Comments
 (0)