Skip to content

Commit 38bac42

Browse files
tniessenBridgeAR
authored andcommitted
crypto: allow passing null as IV unless required
PR-URL: nodejs#18644 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent cf52ab1 commit 38bac42

File tree

4 files changed

+51
-15
lines changed

4 files changed

+51
-15
lines changed

doc/api/crypto.md

+14-2
Original file line numberDiff line numberDiff line change
@@ -1286,6 +1286,11 @@ Adversaries][] for details.
12861286
### crypto.createCipheriv(algorithm, key, iv[, options])
12871287
<!-- YAML
12881288
added: v0.1.94
1289+
changes:
1290+
- version: REPLACEME
1291+
pr-url: https://github.com/nodejs/node/pull/18644
1292+
description: The `iv` parameter may now be `null` for ciphers which do not
1293+
need an initialization vector.
12891294
-->
12901295
- `algorithm` {string}
12911296
- `key` {string | Buffer | TypedArray | DataView}
@@ -1301,7 +1306,8 @@ available cipher algorithms.
13011306

13021307
The `key` is the raw key used by the `algorithm` and `iv` is an
13031308
[initialization vector][]. Both arguments must be `'utf8'` encoded strings,
1304-
[Buffers][`Buffer`], `TypedArray`, or `DataView`s.
1309+
[Buffers][`Buffer`], `TypedArray`, or `DataView`s. If the cipher does not need
1310+
an initialization vector, `iv` may be `null`.
13051311

13061312
### crypto.createCredentials(details)
13071313
<!-- YAML
@@ -1347,6 +1353,11 @@ to create the `Decipher` object.
13471353
### crypto.createDecipheriv(algorithm, key, iv[, options])
13481354
<!-- YAML
13491355
added: v0.1.94
1356+
changes:
1357+
- version: REPLACEME
1358+
pr-url: https://github.com/nodejs/node/pull/18644
1359+
description: The `iv` parameter may now be `null` for ciphers which do not
1360+
need an initialization vector.
13501361
-->
13511362
- `algorithm` {string}
13521363
- `key` {string | Buffer | TypedArray | DataView}
@@ -1363,7 +1374,8 @@ available cipher algorithms.
13631374

13641375
The `key` is the raw key used by the `algorithm` and `iv` is an
13651376
[initialization vector][]. Both arguments must be `'utf8'` encoded strings,
1366-
[Buffers][`Buffer`], `TypedArray`, or `DataView`s.
1377+
[Buffers][`Buffer`], `TypedArray`, or `DataView`s. If the cipher does not need
1378+
an initialization vector, `iv` may be `null`.
13671379

13681380
### crypto.createDiffieHellman(prime[, primeEncoding][, generator][, generatorEncoding])
13691381
<!-- YAML

lib/internal/crypto/cipher.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ function Cipheriv(cipher, key, iv, options) {
182182
}
183183

184184
iv = toBuf(iv);
185-
if (!isArrayBufferView(iv)) {
185+
if (iv !== null && !isArrayBufferView(iv)) {
186186
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'iv',
187187
['string', 'Buffer', 'TypedArray', 'DataView']);
188188
}
@@ -253,7 +253,7 @@ function Decipheriv(cipher, key, iv, options) {
253253
}
254254

255255
iv = toBuf(iv);
256-
if (!isArrayBufferView(iv)) {
256+
if (iv !== null && !isArrayBufferView(iv)) {
257257
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'iv',
258258
['string', 'Buffer', 'TypedArray', 'DataView']);
259259
}

src/node_crypto.cc

+26-8
Original file line numberDiff line numberDiff line change
@@ -3090,8 +3090,17 @@ void CipherBase::InitIv(const char* cipher_type,
30903090
const int expected_iv_len = EVP_CIPHER_iv_length(cipher);
30913091
const int mode = EVP_CIPHER_mode(cipher);
30923092
const bool is_gcm_mode = (EVP_CIPH_GCM_MODE == mode);
3093+
const bool has_iv = iv_len >= 0;
30933094

3094-
if (is_gcm_mode == false && iv_len != expected_iv_len) {
3095+
// Throw if no IV was passed and the cipher requires an IV
3096+
if (!has_iv && expected_iv_len != 0) {
3097+
char msg[128];
3098+
snprintf(msg, sizeof(msg), "Missing IV for cipher %s", cipher_type);
3099+
return env()->ThrowError(msg);
3100+
}
3101+
3102+
// Throw if an IV was passed which does not match the cipher's fixed IV length
3103+
if (is_gcm_mode == false && has_iv && iv_len != expected_iv_len) {
30953104
return env()->ThrowError("Invalid IV length");
30963105
}
30973106

@@ -3103,11 +3112,13 @@ void CipherBase::InitIv(const char* cipher_type,
31033112
const bool encrypt = (kind_ == kCipher);
31043113
EVP_CipherInit_ex(ctx_, cipher, nullptr, nullptr, nullptr, encrypt);
31053114

3106-
if (is_gcm_mode &&
3107-
!EVP_CIPHER_CTX_ctrl(ctx_, EVP_CTRL_GCM_SET_IVLEN, iv_len, nullptr)) {
3108-
EVP_CIPHER_CTX_free(ctx_);
3109-
ctx_ = nullptr;
3110-
return env()->ThrowError("Invalid IV length");
3115+
if (is_gcm_mode) {
3116+
CHECK(has_iv);
3117+
if (!EVP_CIPHER_CTX_ctrl(ctx_, EVP_CTRL_GCM_SET_IVLEN, iv_len, nullptr)) {
3118+
EVP_CIPHER_CTX_free(ctx_);
3119+
ctx_ = nullptr;
3120+
return env()->ThrowError("Invalid IV length");
3121+
}
31113122
}
31123123

31133124
if (!EVP_CIPHER_CTX_set_key_length(ctx_, key_len)) {
@@ -3135,8 +3146,15 @@ void CipherBase::InitIv(const FunctionCallbackInfo<Value>& args) {
31353146
const node::Utf8Value cipher_type(env->isolate(), args[0]);
31363147
ssize_t key_len = Buffer::Length(args[1]);
31373148
const char* key_buf = Buffer::Data(args[1]);
3138-
ssize_t iv_len = Buffer::Length(args[2]);
3139-
const char* iv_buf = Buffer::Data(args[2]);
3149+
ssize_t iv_len;
3150+
const char* iv_buf;
3151+
if (args[2]->IsNull()) {
3152+
iv_buf = nullptr;
3153+
iv_len = -1;
3154+
} else {
3155+
iv_buf = Buffer::Data(args[2]);
3156+
iv_len = Buffer::Length(args[2]);
3157+
}
31403158
cipher->InitIv(*cipher_type, key_buf, key_len, iv_buf, iv_len);
31413159
}
31423160

test/parallel/test-crypto-cipheriv-decipheriv.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ function testCipher3(key, iv) {
105105
});
106106

107107
common.expectsError(
108-
() => crypto.createCipheriv('des-ede3-cbc', key, null),
108+
() => crypto.createCipheriv('des-ede3-cbc', key, 10),
109109
{
110110
code: 'ERR_INVALID_ARG_TYPE',
111111
type: TypeError,
@@ -141,7 +141,7 @@ function testCipher3(key, iv) {
141141
});
142142

143143
common.expectsError(
144-
() => crypto.createDecipheriv('des-ede3-cbc', key, null),
144+
() => crypto.createDecipheriv('des-ede3-cbc', key, 10),
145145
{
146146
code: 'ERR_INVALID_ARG_TYPE',
147147
type: TypeError,
@@ -161,8 +161,9 @@ if (!common.hasFipsCrypto) {
161161
Buffer.from('A6A6A6A6A6A6A6A6', 'hex'));
162162
}
163163

164-
// Zero-sized IV should be accepted in ECB mode.
164+
// Zero-sized IV or null should be accepted in ECB mode.
165165
crypto.createCipheriv('aes-128-ecb', Buffer.alloc(16), Buffer.alloc(0));
166+
crypto.createCipheriv('aes-128-ecb', Buffer.alloc(16), null);
166167

167168
const errMessage = /Invalid IV length/;
168169

@@ -186,6 +187,11 @@ for (let n = 0; n < 256; n += 1) {
186187
errMessage);
187188
}
188189

190+
// And so should null be.
191+
assert.throws(() => {
192+
crypto.createCipheriv('aes-128-cbc', Buffer.alloc(16), null);
193+
}, /Missing IV for cipher aes-128-cbc/);
194+
189195
// Zero-sized IV should be rejected in GCM mode.
190196
assert.throws(
191197
() => crypto.createCipheriv('aes-128-gcm', Buffer.alloc(16),

0 commit comments

Comments
 (0)