@@ -210,8 +210,7 @@ ParseKeyResult ParsePrivateKey(EVPKeyPointer* pkey,
210210 const PrivateKeyEncodingConfig& config,
211211 const char * key,
212212 size_t key_len) {
213- // OpenSSL needs a non-const pointer, that's why the const_cast is required.
214- char * const passphrase = const_cast <char *>(config.passphrase_ .get ());
213+ const ByteSource* passphrase = config.passphrase_ .get ();
215214
216215 if (config.format_ == kKeyFormatPEM ) {
217216 BIOPointer bio (BIO_new_mem_buf (key, key_len));
@@ -221,7 +220,7 @@ ParseKeyResult ParsePrivateKey(EVPKeyPointer* pkey,
221220 pkey->reset (PEM_read_bio_PrivateKey (bio.get (),
222221 nullptr ,
223222 PasswordCallback,
224- passphrase));
223+ & passphrase));
225224 } else {
226225 CHECK_EQ (config.format_ , kKeyFormatDER );
227226
@@ -238,7 +237,7 @@ ParseKeyResult ParsePrivateKey(EVPKeyPointer* pkey,
238237 pkey->reset (d2i_PKCS8PrivateKey_bio (bio.get (),
239238 nullptr ,
240239 PasswordCallback,
241- passphrase));
240+ & passphrase));
242241 } else {
243242 PKCS8Pointer p8inf (d2i_PKCS8_PRIV_KEY_INFO_bio (bio.get (), nullptr ));
244243 if (p8inf)
@@ -260,7 +259,7 @@ ParseKeyResult ParsePrivateKey(EVPKeyPointer* pkey,
260259 return ParseKeyResult::kParseKeyOk ;
261260 if (ERR_GET_LIB (err) == ERR_LIB_PEM &&
262261 ERR_GET_REASON (err) == PEM_R_BAD_PASSWORD_READ) {
263- if (config.passphrase_ .get () == nullptr )
262+ if (config.passphrase_ .IsEmpty () )
264263 return ParseKeyResult::kParseKeyNeedPassphrase ;
265264 }
266265 return ParseKeyResult::kParseKeyFailed ;
@@ -293,6 +292,28 @@ MaybeLocal<Value> WritePrivateKey(
293292 BIOPointer bio (BIO_new (BIO_s_mem ()));
294293 CHECK (bio);
295294
295+ // If an empty string was passed as the passphrase, the ByteSource might
296+ // contain a null pointer, which OpenSSL will ignore, causing it to invoke its
297+ // default passphrase callback, which would block the thread until the user
298+ // manually enters a passphrase. We could supply our own passphrase callback
299+ // to handle this special case, but it is easier to avoid passing a null
300+ // pointer to OpenSSL.
301+ char * pass = nullptr ;
302+ size_t pass_len = 0 ;
303+ if (!config.passphrase_ .IsEmpty ()) {
304+ pass = const_cast <char *>(config.passphrase_ ->get ());
305+ pass_len = config.passphrase_ ->size ();
306+ if (pass == nullptr ) {
307+ // OpenSSL will not actually dereference this pointer, so it can be any
308+ // non-null pointer. We cannot assert that directly, which is why we
309+ // intentionally use a pointer that will likely cause a segmentation fault
310+ // when dereferenced.
311+ CHECK_EQ (pass_len, 0 );
312+ pass = reinterpret_cast <char *>(-1 );
313+ CHECK_NE (pass, nullptr );
314+ }
315+ }
316+
296317 bool err;
297318
298319 PKEncodingType encoding_type = config.type_ .ToChecked ();
@@ -303,12 +324,11 @@ MaybeLocal<Value> WritePrivateKey(
303324 RSAPointer rsa (EVP_PKEY_get1_RSA (pkey));
304325 if (config.format_ == kKeyFormatPEM ) {
305326 // Encode PKCS#1 as PEM.
306- const char * pass = config.passphrase_ .get ();
307327 err = PEM_write_bio_RSAPrivateKey (
308328 bio.get (), rsa.get (),
309329 config.cipher_ ,
310- reinterpret_cast <unsigned char *>(const_cast < char *>( pass) ),
311- config. passphrase_ . size () ,
330+ reinterpret_cast <unsigned char *>(pass),
331+ pass_len ,
312332 nullptr , nullptr ) != 1 ;
313333 } else {
314334 // Encode PKCS#1 as DER. This does not permit encryption.
@@ -322,17 +342,17 @@ MaybeLocal<Value> WritePrivateKey(
322342 err = PEM_write_bio_PKCS8PrivateKey (
323343 bio.get (), pkey,
324344 config.cipher_ ,
325- const_cast < char *>(config. passphrase_ . get ()) ,
326- config. passphrase_ . size () ,
345+ pass ,
346+ pass_len ,
327347 nullptr , nullptr ) != 1 ;
328348 } else {
329349 // Encode PKCS#8 as DER.
330350 CHECK_EQ (config.format_ , kKeyFormatDER );
331351 err = i2d_PKCS8PrivateKey_bio (
332352 bio.get (), pkey,
333353 config.cipher_ ,
334- const_cast < char *>(config. passphrase_ . get ()) ,
335- config. passphrase_ . size () ,
354+ pass ,
355+ pass_len ,
336356 nullptr , nullptr ) != 1 ;
337357 }
338358 } else {
@@ -344,12 +364,11 @@ MaybeLocal<Value> WritePrivateKey(
344364 ECKeyPointer ec_key (EVP_PKEY_get1_EC_KEY (pkey));
345365 if (config.format_ == kKeyFormatPEM ) {
346366 // Encode SEC1 as PEM.
347- const char * pass = config.passphrase_ .get ();
348367 err = PEM_write_bio_ECPrivateKey (
349368 bio.get (), ec_key.get (),
350369 config.cipher_ ,
351- reinterpret_cast <unsigned char *>(const_cast < char *>( pass) ),
352- config. passphrase_ . size () ,
370+ reinterpret_cast <unsigned char *>(pass),
371+ pass_len ,
353372 nullptr , nullptr ) != 1 ;
354373 } else {
355374 // Encode SEC1 as DER. This does not permit encryption.
@@ -640,7 +659,8 @@ ManagedEVPPKey::GetPrivateKeyEncodingFromJs(
640659 THROW_ERR_OUT_OF_RANGE (env, " passphrase is too big" );
641660 return NonCopyableMaybe<PrivateKeyEncodingConfig>();
642661 }
643- result.passphrase_ = passphrase.ToNullTerminatedCopy ();
662+ result.passphrase_ = NonCopyableMaybe<ByteSource>(
663+ passphrase.ToNullTerminatedCopy ());
644664 } else {
645665 CHECK (args[*offset]->IsNullOrUndefined () && !needs_passphrase);
646666 }
0 commit comments