Repository: nodejs/node
Pull request: crypto: remove CipherBase::Init

As far as I can tell, the `iv` parameter can never be `undefined` (but it can be `null`!), so this code appears to have been dead since Node.js 22.

This change removes dead code and adds a tiny test case for passing `undefined` as the IV.

Refs: https://github.com/nodejs/node/pull/50973

<!--
Before submitting a pull request, please read:

- the CONTRIBUTING guide at https://github.com/nodejs/node/blob/HEAD/CONTRIBUTING.md
- the commit message formatting guidelines at
  https://github.com/nodejs/node/blob/HEAD/doc/contributing/pull-requests.md#commit-message-guidelines

For code changes:
1. Include tests for any bug fixes or new features.
2. Update documentation if relevant.
3. Ensure that `make -j4 test` (UNIX), or `vcbuild test` (Windows) passes.

If you believe this PR should be highlighted in the Node.js CHANGELOG
please add the `notable-change` label.

Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.
-->


Changed files:
filename: lib/internal/crypto/cipher.js
patch: @@ -115,11 +115,7 @@ function getUIntOption(options, key) {
 function createCipherBase(cipher, credential, options, decipher, iv) {
   const authTagLength = getUIntOption(options, 'authTagLength');
   this[kHandle] = new CipherBase(decipher);
-  if (iv === undefined) {
-    this[kHandle].init(cipher, credential, authTagLength);
-  } else {
-    this[kHandle].initiv(cipher, credential, iv, authTagLength);
-  }
+  this[kHandle].initiv(cipher, credential, iv, authTagLength);
   this._decoder = null;
 
   ReflectApply(LazyTransform, this, [options]);

filename: src/crypto/crypto_cipher.cc
patch: @@ -231,7 +231,6 @@ void CipherBase::Initialize(Environment* env, Local<Object> target) {
 
   t->InstanceTemplate()->SetInternalFieldCount(CipherBase::kInternalFieldCount);
 
-  SetProtoMethod(isolate, t, "init", Init);
   SetProtoMethod(isolate, t, "initiv", InitIv);
   SetProtoMethod(isolate, t, "update", Update);
   SetProtoMethod(isolate, t, "final", Final);
@@ -275,7 +274,6 @@ void CipherBase::RegisterExternalReferences(
     ExternalReferenceRegistry* registry) {
   registry->Register(New);
 
-  registry->Register(Init);
   registry->Register(InitIv);
   registry->Register(Update);
   registry->Register(Final);
@@ -347,69 +345,6 @@ void CipherBase::CommonInit(std::string_view cipher_type,
   }
 }
 
-void CipherBase::Init(std::string_view cipher_type,
-                      const ArrayBufferOrViewContents<unsigned char>& key_buf,
-                      unsigned int auth_tag_len) {
-  HandleScope scope(env()->isolate());
-  MarkPopErrorOnReturn mark_pop_error_on_return;
-  const auto cipher = Cipher::FromName(cipher_type);
-  if (!cipher) {
-    return THROW_ERR_CRYPTO_UNKNOWN_CIPHER(env());
-  }
-
-  unsigned char key[Cipher::MAX_KEY_LENGTH];
-  unsigned char iv[Cipher::MAX_IV_LENGTH];
-
-  ncrypto::Buffer<const unsigned char> keyBuf{
-      .data = key_buf.data(),
-      .len = key_buf.size(),
-  };
-  int key_len = cipher.bytesToKey(Digest::MD5, keyBuf, key, iv);
-  CHECK_NE(key_len, 0);
-
-  if (kind_ == kCipher &&
-      (cipher.isCtrMode() || cipher.isGcmMode() || cipher.isCcmMode())) {
-    // Ignore the return value (i.e. possible exception) because we are
-    // not calling back into JS anyway.
-    ProcessEmitWarning(env(),
-                       "Use Cipheriv for counter mode of %s",
-                       cipher_type);
-  }
-
-  CommonInit(cipher_type,
-             cipher,
-             key,
-             key_len,
-             iv,
-             cipher.getIvLength(),
-             auth_tag_len);
-}
-
-void CipherBase::Init(const FunctionCallbackInfo<Value>& args) {
-  CipherBase* cipher;
-  ASSIGN_OR_RETURN_UNWRAP(&cipher, args.This());
-  Environment* env = Environment::GetCurrent(args);
-
-  CHECK_GE(args.Length(), 3);
-
-  const Utf8Value cipher_type(args.GetIsolate(), args[0]);
-  ArrayBufferOrViewContents<unsigned char> key_buf(args[1]);
-  if (!key_buf.CheckSizeInt32())
-    return THROW_ERR_OUT_OF_RANGE(env, "password is too large");
-
-  // Don't assign to cipher->auth_tag_len_ directly; the value might not
-  // represent a valid length at this point.
-  unsigned int auth_tag_len;
-  if (args[2]->IsUint32()) {
-    auth_tag_len = args[2].As<Uint32>()->Value();
-  } else {
-    CHECK(args[2]->IsInt32() && args[2].As<Int32>()->Value() == -1);
-    auth_tag_len = kNoAuthTagLength;
-  }
-
-  cipher->Init(cipher_type.ToStringView(), key_buf, auth_tag_len);
-}
-
 void CipherBase::InitIv(std::string_view cipher_type,
                         const ByteSource& key_buf,
                         const ArrayBufferOrViewContents<unsigned char>& iv_buf,

filename: src/crypto/crypto_cipher.h
patch: @@ -50,9 +50,6 @@ class CipherBase : public BaseObject {
                   const unsigned char* iv,
                   int iv_len,
                   unsigned int auth_tag_len);
-  void Init(std::string_view cipher_type,
-            const ArrayBufferOrViewContents<unsigned char>& key_buf,
-            unsigned int auth_tag_len);
   void InitIv(std::string_view cipher_type,
               const ByteSource& key_buf,
               const ArrayBufferOrViewContents<unsigned char>& iv_buf,
@@ -73,7 +70,6 @@ class CipherBase : public BaseObject {
   bool MaybePassAuthTagToOpenSSL();
 
   static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
-  static void Init(const v8::FunctionCallbackInfo<v8::Value>& args);
   static void InitIv(const v8::FunctionCallbackInfo<v8::Value>& args);
   static void Update(const v8::FunctionCallbackInfo<v8::Value>& args);
   static void Final(const v8::FunctionCallbackInfo<v8::Value>& args);

filename: test/parallel/test-crypto-cipheriv-decipheriv.js
patch: @@ -171,6 +171,14 @@ for (let n = 1; n < 256; n += 1) {
     errMessage);
 }
 
+// And so should undefined be (regardless of mode).
+assert.throws(
+  () => crypto.createCipheriv('aes-128-ecb', Buffer.alloc(16)),
+  { code: 'ERR_INVALID_ARG_TYPE' });
+assert.throws(
+  () => crypto.createCipheriv('aes-128-ecb', Buffer.alloc(16), undefined),
+  { code: 'ERR_INVALID_ARG_TYPE' });
+
 // Correctly sized IV should be accepted in CBC mode.
 crypto.createCipheriv('aes-128-cbc', Buffer.alloc(16), Buffer.alloc(16));