Skip to content

Commit 0a776f3

Browse files
committed
crypto: allow passing null as IV unless required
PR-URL: nodejs#18644 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent fed51b3 commit 0a776f3

3 files changed

Lines changed: 51 additions & 12 deletions

File tree

doc/api/crypto.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,6 +1273,11 @@ Adversaries][] for details.
12731273
### crypto.createCipheriv(algorithm, key, iv[, options])
12741274
<!-- YAML
12751275
added: v0.1.94
1276+
changes:
1277+
- version: REPLACEME
1278+
pr-url: https://github.com/nodejs/node/pull/18644
1279+
description: The `iv` parameter may now be `null` for ciphers which do not
1280+
need an initialization vector.
12761281
-->
12771282
- `algorithm` {string}
12781283
- `key` {string | Buffer | TypedArray | DataView}
@@ -1288,7 +1293,8 @@ available cipher algorithms.
12881293

12891294
The `key` is the raw key used by the `algorithm` and `iv` is an
12901295
[initialization vector][]. Both arguments must be `'utf8'` encoded strings,
1291-
[Buffers][`Buffer`], `TypedArray`, or `DataView`s.
1296+
[Buffers][`Buffer`], `TypedArray`, or `DataView`s. If the cipher does not need
1297+
an initialization vector, `iv` may be `null`.
12921298

12931299
### crypto.createCredentials(details)
12941300
<!-- YAML
@@ -1334,6 +1340,11 @@ to create the `Decipher` object.
13341340
### crypto.createDecipheriv(algorithm, key, iv[, options])
13351341
<!-- YAML
13361342
added: v0.1.94
1343+
changes:
1344+
- version: REPLACEME
1345+
pr-url: https://github.com/nodejs/node/pull/18644
1346+
description: The `iv` parameter may now be `null` for ciphers which do not
1347+
need an initialization vector.
13371348
-->
13381349
- `algorithm` {string}
13391350
- `key` {string | Buffer | TypedArray | DataView}
@@ -1350,7 +1361,8 @@ available cipher algorithms.
13501361

13511362
The `key` is the raw key used by the `algorithm` and `iv` is an
13521363
[initialization vector][]. Both arguments must be `'utf8'` encoded strings,
1353-
[Buffers][`Buffer`], `TypedArray`, or `DataView`s.
1364+
[Buffers][`Buffer`], `TypedArray`, or `DataView`s. If the cipher does not need
1365+
an initialization vector, `iv` may be `null`.
13541366

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

src/node_crypto.cc

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3760,8 +3760,17 @@ void CipherBase::InitIv(const char* cipher_type,
37603760
const int expected_iv_len = EVP_CIPHER_iv_length(cipher);
37613761
const int mode = EVP_CIPHER_mode(cipher);
37623762
const bool is_gcm_mode = (EVP_CIPH_GCM_MODE == mode);
3763+
const bool has_iv = iv_len >= 0;
37633764

3764-
if (is_gcm_mode == false && iv_len != expected_iv_len) {
3765+
// Throw if no IV was passed and the cipher requires an IV
3766+
if (!has_iv && expected_iv_len != 0) {
3767+
char msg[128];
3768+
snprintf(msg, sizeof(msg), "Missing IV for cipher %s", cipher_type);
3769+
return env()->ThrowError(msg);
3770+
}
3771+
3772+
// Throw if an IV was passed which does not match the cipher's fixed IV length
3773+
if (is_gcm_mode == false && has_iv && iv_len != expected_iv_len) {
37653774
return env()->ThrowError("Invalid IV length");
37663775
}
37673776

@@ -3773,11 +3782,13 @@ void CipherBase::InitIv(const char* cipher_type,
37733782
const bool encrypt = (kind_ == kCipher);
37743783
EVP_CipherInit_ex(ctx_, cipher, nullptr, nullptr, nullptr, encrypt);
37753784

3776-
if (is_gcm_mode &&
3777-
!EVP_CIPHER_CTX_ctrl(ctx_, EVP_CTRL_GCM_SET_IVLEN, iv_len, nullptr)) {
3778-
EVP_CIPHER_CTX_free(ctx_);
3779-
ctx_ = nullptr;
3780-
return env()->ThrowError("Invalid IV length");
3785+
if (is_gcm_mode) {
3786+
CHECK(has_iv);
3787+
if (!EVP_CIPHER_CTX_ctrl(ctx_, EVP_CTRL_GCM_SET_IVLEN, iv_len, nullptr)) {
3788+
EVP_CIPHER_CTX_free(ctx_);
3789+
ctx_ = nullptr;
3790+
return env()->ThrowError("Invalid IV length");
3791+
}
37813792
}
37823793

37833794
if (!EVP_CIPHER_CTX_set_key_length(ctx_, key_len)) {
@@ -3806,13 +3817,23 @@ void CipherBase::InitIv(const FunctionCallbackInfo<Value>& args) {
38063817

38073818
THROW_AND_RETURN_IF_NOT_STRING(args[0], "Cipher type");
38083819
THROW_AND_RETURN_IF_NOT_BUFFER(args[1], "Key");
3809-
THROW_AND_RETURN_IF_NOT_BUFFER(args[2], "IV");
3820+
3821+
if (!args[2]->IsNull() && !Buffer::HasInstance(args[2])) {
3822+
return env->ThrowTypeError("IV must be a buffer");
3823+
}
38103824

38113825
const node::Utf8Value cipher_type(env->isolate(), args[0]);
38123826
ssize_t key_len = Buffer::Length(args[1]);
38133827
const char* key_buf = Buffer::Data(args[1]);
3814-
ssize_t iv_len = Buffer::Length(args[2]);
3815-
const char* iv_buf = Buffer::Data(args[2]);
3828+
ssize_t iv_len;
3829+
const char* iv_buf;
3830+
if (args[2]->IsNull()) {
3831+
iv_buf = nullptr;
3832+
iv_len = -1;
3833+
} else {
3834+
iv_buf = Buffer::Data(args[2]);
3835+
iv_len = Buffer::Length(args[2]);
3836+
}
38163837
cipher->InitIv(*cipher_type, key_buf, key_len, iv_buf, iv_len);
38173838
}
38183839

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,9 @@ if (!common.hasFipsCrypto) {
8989
Buffer.from('A6A6A6A6A6A6A6A6', 'hex'));
9090
}
9191

92-
// Zero-sized IV should be accepted in ECB mode.
92+
// Zero-sized IV or null should be accepted in ECB mode.
9393
crypto.createCipheriv('aes-128-ecb', Buffer.alloc(16), Buffer.alloc(0));
94+
crypto.createCipheriv('aes-128-ecb', Buffer.alloc(16), null);
9495

9596
const errMessage = /Invalid IV length/;
9697

@@ -114,6 +115,11 @@ for (let n = 0; n < 256; n += 1) {
114115
errMessage);
115116
}
116117

118+
// And so should null be.
119+
assert.throws(() => {
120+
crypto.createCipheriv('aes-128-cbc', Buffer.alloc(16), null);
121+
}, /Missing IV for cipher aes-128-cbc/);
122+
117123
// Zero-sized IV should be rejected in GCM mode.
118124
assert.throws(
119125
() => crypto.createCipheriv('aes-128-gcm', Buffer.alloc(16),

0 commit comments

Comments
 (0)