@@ -210,8 +210,7 @@ ParseKeyResult ParsePrivateKey(EVPKeyPointer* pkey,
210
210
const PrivateKeyEncodingConfig& config,
211
211
const char * key,
212
212
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 ();
215
214
216
215
if (config.format_ == kKeyFormatPEM ) {
217
216
BIOPointer bio (BIO_new_mem_buf (key, key_len));
@@ -221,7 +220,7 @@ ParseKeyResult ParsePrivateKey(EVPKeyPointer* pkey,
221
220
pkey->reset (PEM_read_bio_PrivateKey (bio.get (),
222
221
nullptr ,
223
222
PasswordCallback,
224
- passphrase));
223
+ & passphrase));
225
224
} else {
226
225
CHECK_EQ (config.format_ , kKeyFormatDER );
227
226
@@ -238,7 +237,7 @@ ParseKeyResult ParsePrivateKey(EVPKeyPointer* pkey,
238
237
pkey->reset (d2i_PKCS8PrivateKey_bio (bio.get (),
239
238
nullptr ,
240
239
PasswordCallback,
241
- passphrase));
240
+ & passphrase));
242
241
} else {
243
242
PKCS8Pointer p8inf (d2i_PKCS8_PRIV_KEY_INFO_bio (bio.get (), nullptr ));
244
243
if (p8inf)
@@ -260,7 +259,7 @@ ParseKeyResult ParsePrivateKey(EVPKeyPointer* pkey,
260
259
return ParseKeyResult::kParseKeyOk ;
261
260
if (ERR_GET_LIB (err) == ERR_LIB_PEM &&
262
261
ERR_GET_REASON (err) == PEM_R_BAD_PASSWORD_READ) {
263
- if (config.passphrase_ .get () == nullptr )
262
+ if (config.passphrase_ .IsEmpty () )
264
263
return ParseKeyResult::kParseKeyNeedPassphrase ;
265
264
}
266
265
return ParseKeyResult::kParseKeyFailed ;
@@ -293,6 +292,28 @@ MaybeLocal<Value> WritePrivateKey(
293
292
BIOPointer bio (BIO_new (BIO_s_mem ()));
294
293
CHECK (bio);
295
294
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
+
296
317
bool err;
297
318
298
319
PKEncodingType encoding_type = config.type_ .ToChecked ();
@@ -303,12 +324,11 @@ MaybeLocal<Value> WritePrivateKey(
303
324
RSAPointer rsa (EVP_PKEY_get1_RSA (pkey));
304
325
if (config.format_ == kKeyFormatPEM ) {
305
326
// Encode PKCS#1 as PEM.
306
- const char * pass = config.passphrase_ .get ();
307
327
err = PEM_write_bio_RSAPrivateKey (
308
328
bio.get (), rsa.get (),
309
329
config.cipher_ ,
310
- reinterpret_cast <unsigned char *>(const_cast < char *>( pass) ),
311
- config. passphrase_ . size () ,
330
+ reinterpret_cast <unsigned char *>(pass),
331
+ pass_len ,
312
332
nullptr , nullptr ) != 1 ;
313
333
} else {
314
334
// Encode PKCS#1 as DER. This does not permit encryption.
@@ -322,17 +342,17 @@ MaybeLocal<Value> WritePrivateKey(
322
342
err = PEM_write_bio_PKCS8PrivateKey (
323
343
bio.get (), pkey,
324
344
config.cipher_ ,
325
- const_cast < char *>(config. passphrase_ . get ()) ,
326
- config. passphrase_ . size () ,
345
+ pass ,
346
+ pass_len ,
327
347
nullptr , nullptr ) != 1 ;
328
348
} else {
329
349
// Encode PKCS#8 as DER.
330
350
CHECK_EQ (config.format_ , kKeyFormatDER );
331
351
err = i2d_PKCS8PrivateKey_bio (
332
352
bio.get (), pkey,
333
353
config.cipher_ ,
334
- const_cast < char *>(config. passphrase_ . get ()) ,
335
- config. passphrase_ . size () ,
354
+ pass ,
355
+ pass_len ,
336
356
nullptr , nullptr ) != 1 ;
337
357
}
338
358
} else {
@@ -344,12 +364,11 @@ MaybeLocal<Value> WritePrivateKey(
344
364
ECKeyPointer ec_key (EVP_PKEY_get1_EC_KEY (pkey));
345
365
if (config.format_ == kKeyFormatPEM ) {
346
366
// Encode SEC1 as PEM.
347
- const char * pass = config.passphrase_ .get ();
348
367
err = PEM_write_bio_ECPrivateKey (
349
368
bio.get (), ec_key.get (),
350
369
config.cipher_ ,
351
- reinterpret_cast <unsigned char *>(const_cast < char *>( pass) ),
352
- config. passphrase_ . size () ,
370
+ reinterpret_cast <unsigned char *>(pass),
371
+ pass_len ,
353
372
nullptr , nullptr ) != 1 ;
354
373
} else {
355
374
// Encode SEC1 as DER. This does not permit encryption.
@@ -640,7 +659,8 @@ ManagedEVPPKey::GetPrivateKeyEncodingFromJs(
640
659
THROW_ERR_OUT_OF_RANGE (env, " passphrase is too big" );
641
660
return NonCopyableMaybe<PrivateKeyEncodingConfig>();
642
661
}
643
- result.passphrase_ = passphrase.ToNullTerminatedCopy ();
662
+ result.passphrase_ = NonCopyableMaybe<ByteSource>(
663
+ passphrase.ToNullTerminatedCopy ());
644
664
} else {
645
665
CHECK (args[*offset]->IsNullOrUndefined () && !needs_passphrase);
646
666
}
0 commit comments