Skip to content

Commit 2e2c015

Browse files
committedMar 30, 2019
crypto: decode missing passphrase errors
When a user attempts to load an encrypted key without supplying a passphrase, a cryptic OpenSSL error is thrown. This change intercepts the OpenSSL error and throws a nice error code instead. PR-URL: #25208 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
1 parent 61ef9df commit 2e2c015

File tree

7 files changed

+191
-79
lines changed

7 files changed

+191
-79
lines changed
 

‎doc/api/errors.md

+5
Original file line numberDiff line numberDiff line change
@@ -1486,6 +1486,11 @@ a `dynamicInstantiate` hook.
14861486
A `MessagePort` was found in the object passed to a `postMessage()` call,
14871487
but not provided in the `transferList` for that call.
14881488

1489+
<a id="ERR_MISSING_PASSPHRASE"></a>
1490+
### ERR_MISSING_PASSPHRASE
1491+
1492+
An attempt was made to read an encrypted key without specifying a passphrase.
1493+
14891494
<a id="ERR_MISSING_PLATFORM_FOR_WORKER"></a>
14901495
### ERR_MISSING_PLATFORM_FOR_WORKER
14911496

‎src/node_crypto.cc

+112-65
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,33 @@ template int SSLWrap<TLSWrap>::SelectALPNCallback(
153153
unsigned int inlen,
154154
void* arg);
155155

156+
class PasswordCallbackInfo {
157+
public:
158+
explicit PasswordCallbackInfo(const char* passphrase)
159+
: passphrase_(passphrase) {}
160+
161+
inline const char* GetPassword() {
162+
needs_passphrase_ = true;
163+
return passphrase_;
164+
}
165+
166+
inline bool CalledButEmpty() {
167+
return needs_passphrase_ && passphrase_ == nullptr;
168+
}
169+
170+
private:
171+
const char* passphrase_;
172+
bool needs_passphrase_ = false;
173+
};
156174

157175
static int PasswordCallback(char* buf, int size, int rwflag, void* u) {
158-
if (u) {
176+
PasswordCallbackInfo* info = static_cast<PasswordCallbackInfo*>(u);
177+
const char* passphrase = info->GetPassword();
178+
if (passphrase != nullptr) {
159179
size_t buflen = static_cast<size_t>(size);
160-
size_t len = strlen(static_cast<const char*>(u));
180+
size_t len = strlen(passphrase);
161181
len = len > buflen ? buflen : len;
162-
memcpy(buf, u, len);
182+
memcpy(buf, passphrase, len);
163183
return len;
164184
}
165185

@@ -698,11 +718,12 @@ void SecureContext::SetKey(const FunctionCallbackInfo<Value>& args) {
698718

699719
node::Utf8Value passphrase(env->isolate(), args[1]);
700720

721+
PasswordCallbackInfo cb_info(len == 1 ? nullptr : *passphrase);
701722
EVPKeyPointer key(
702723
PEM_read_bio_PrivateKey(bio.get(),
703724
nullptr,
704725
PasswordCallback,
705-
len == 1 ? nullptr : *passphrase));
726+
&cb_info));
706727

707728
if (!key) {
708729
unsigned long err = ERR_get_error(); // NOLINT(runtime/int)
@@ -2899,13 +2920,14 @@ static bool IsSupportedAuthenticatedMode(const EVP_CIPHER_CTX* ctx) {
28992920
return IsSupportedAuthenticatedMode(cipher);
29002921
}
29012922

2902-
enum class ParsePublicKeyResult {
2903-
kParsePublicOk,
2904-
kParsePublicNotRecognized,
2905-
kParsePublicFailed
2923+
enum class ParseKeyResult {
2924+
kParseKeyOk,
2925+
kParseKeyNotRecognized,
2926+
kParseKeyNeedPassphrase,
2927+
kParseKeyFailed
29062928
};
29072929

2908-
static ParsePublicKeyResult TryParsePublicKey(
2930+
static ParseKeyResult TryParsePublicKey(
29092931
EVPKeyPointer* pkey,
29102932
const BIOPointer& bp,
29112933
const char* name,
@@ -2919,33 +2941,33 @@ static ParsePublicKeyResult TryParsePublicKey(
29192941
MarkPopErrorOnReturn mark_pop_error_on_return;
29202942
if (PEM_bytes_read_bio(&der_data, &der_len, nullptr, name,
29212943
bp.get(), nullptr, nullptr) != 1)
2922-
return ParsePublicKeyResult::kParsePublicNotRecognized;
2944+
return ParseKeyResult::kParseKeyNotRecognized;
29232945
}
29242946

29252947
// OpenSSL might modify the pointer, so we need to make a copy before parsing.
29262948
const unsigned char* p = der_data;
29272949
pkey->reset(parse(&p, der_len));
29282950
OPENSSL_clear_free(der_data, der_len);
29292951

2930-
return *pkey ? ParsePublicKeyResult::kParsePublicOk :
2931-
ParsePublicKeyResult::kParsePublicFailed;
2952+
return *pkey ? ParseKeyResult::kParseKeyOk :
2953+
ParseKeyResult::kParseKeyFailed;
29322954
}
29332955

2934-
static ParsePublicKeyResult ParsePublicKeyPEM(EVPKeyPointer* pkey,
2935-
const char* key_pem,
2936-
int key_pem_len) {
2956+
static ParseKeyResult ParsePublicKeyPEM(EVPKeyPointer* pkey,
2957+
const char* key_pem,
2958+
int key_pem_len) {
29372959
BIOPointer bp(BIO_new_mem_buf(const_cast<char*>(key_pem), key_pem_len));
29382960
if (!bp)
2939-
return ParsePublicKeyResult::kParsePublicFailed;
2961+
return ParseKeyResult::kParseKeyFailed;
29402962

2941-
ParsePublicKeyResult ret;
2963+
ParseKeyResult ret;
29422964

29432965
// Try parsing as a SubjectPublicKeyInfo first.
29442966
ret = TryParsePublicKey(pkey, bp, "PUBLIC KEY",
29452967
[](const unsigned char** p, long l) { // NOLINT(runtime/int)
29462968
return d2i_PUBKEY(nullptr, p, l);
29472969
});
2948-
if (ret != ParsePublicKeyResult::kParsePublicNotRecognized)
2970+
if (ret != ParseKeyResult::kParseKeyNotRecognized)
29492971
return ret;
29502972

29512973
// Maybe it is PKCS#1.
@@ -2954,7 +2976,7 @@ static ParsePublicKeyResult ParsePublicKeyPEM(EVPKeyPointer* pkey,
29542976
[](const unsigned char** p, long l) { // NOLINT(runtime/int)
29552977
return d2i_PublicKey(EVP_PKEY_RSA, nullptr, p, l);
29562978
});
2957-
if (ret != ParsePublicKeyResult::kParsePublicNotRecognized)
2979+
if (ret != ParseKeyResult::kParseKeyNotRecognized)
29582980
return ret;
29592981

29602982
// X.509 fallback.
@@ -2966,25 +2988,25 @@ static ParsePublicKeyResult ParsePublicKeyPEM(EVPKeyPointer* pkey,
29662988
});
29672989
}
29682990

2969-
static bool ParsePublicKey(EVPKeyPointer* pkey,
2970-
const PublicKeyEncodingConfig& config,
2971-
const char* key,
2972-
size_t key_len) {
2991+
static ParseKeyResult ParsePublicKey(EVPKeyPointer* pkey,
2992+
const PublicKeyEncodingConfig& config,
2993+
const char* key,
2994+
size_t key_len) {
29732995
if (config.format_ == kKeyFormatPEM) {
2974-
ParsePublicKeyResult r =
2975-
ParsePublicKeyPEM(pkey, key, key_len);
2976-
return r == ParsePublicKeyResult::kParsePublicOk;
2996+
return ParsePublicKeyPEM(pkey, key, key_len);
29772997
} else {
29782998
CHECK_EQ(config.format_, kKeyFormatDER);
2999+
29793000
const unsigned char* p = reinterpret_cast<const unsigned char*>(key);
29803001
if (config.type_.ToChecked() == kKeyEncodingPKCS1) {
29813002
pkey->reset(d2i_PublicKey(EVP_PKEY_RSA, nullptr, &p, key_len));
2982-
return pkey;
29833003
} else {
29843004
CHECK_EQ(config.type_.ToChecked(), kKeyEncodingSPKI);
29853005
pkey->reset(d2i_PUBKEY(nullptr, &p, key_len));
2986-
return pkey;
29873006
}
3007+
3008+
return *pkey ? ParseKeyResult::kParseKeyOk :
3009+
ParseKeyResult::kParseKeyFailed;
29883010
}
29893011
}
29903012

@@ -3099,56 +3121,59 @@ static bool IsEncryptedPrivateKeyInfo(const unsigned char* data, size_t size) {
30993121
data[offset] != 2;
31003122
}
31013123

3102-
static EVPKeyPointer ParsePrivateKey(const PrivateKeyEncodingConfig& config,
3103-
const char* key,
3104-
size_t key_len) {
3105-
EVPKeyPointer pkey;
3124+
static ParseKeyResult ParsePrivateKey(EVPKeyPointer* pkey,
3125+
const PrivateKeyEncodingConfig& config,
3126+
const char* key,
3127+
size_t key_len) {
3128+
PasswordCallbackInfo pc_info(config.passphrase_.get());
31063129

31073130
if (config.format_ == kKeyFormatPEM) {
31083131
BIOPointer bio(BIO_new_mem_buf(key, key_len));
31093132
if (!bio)
3110-
return pkey;
3133+
return ParseKeyResult::kParseKeyFailed;
31113134

3112-
char* pass = const_cast<char*>(config.passphrase_.get());
3113-
pkey.reset(PEM_read_bio_PrivateKey(bio.get(),
3114-
nullptr,
3115-
PasswordCallback,
3116-
pass));
3135+
pkey->reset(PEM_read_bio_PrivateKey(bio.get(),
3136+
nullptr,
3137+
PasswordCallback,
3138+
&pc_info));
31173139
} else {
31183140
CHECK_EQ(config.format_, kKeyFormatDER);
31193141

31203142
if (config.type_.ToChecked() == kKeyEncodingPKCS1) {
31213143
const unsigned char* p = reinterpret_cast<const unsigned char*>(key);
3122-
pkey.reset(d2i_PrivateKey(EVP_PKEY_RSA, nullptr, &p, key_len));
3144+
pkey->reset(d2i_PrivateKey(EVP_PKEY_RSA, nullptr, &p, key_len));
31233145
} else if (config.type_.ToChecked() == kKeyEncodingPKCS8) {
31243146
BIOPointer bio(BIO_new_mem_buf(key, key_len));
31253147
if (!bio)
3126-
return pkey;
3148+
return ParseKeyResult::kParseKeyFailed;
31273149

31283150
if (IsEncryptedPrivateKeyInfo(
31293151
reinterpret_cast<const unsigned char*>(key), key_len)) {
3130-
char* pass = const_cast<char*>(config.passphrase_.get());
3131-
pkey.reset(d2i_PKCS8PrivateKey_bio(bio.get(),
3132-
nullptr,
3133-
PasswordCallback,
3134-
pass));
3152+
pkey->reset(d2i_PKCS8PrivateKey_bio(bio.get(),
3153+
nullptr,
3154+
PasswordCallback,
3155+
&pc_info));
31353156
} else {
31363157
PKCS8Pointer p8inf(d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), nullptr));
31373158
if (p8inf)
3138-
pkey.reset(EVP_PKCS82PKEY(p8inf.get()));
3159+
pkey->reset(EVP_PKCS82PKEY(p8inf.get()));
31393160
}
31403161
} else {
31413162
CHECK_EQ(config.type_.ToChecked(), kKeyEncodingSEC1);
31423163
const unsigned char* p = reinterpret_cast<const unsigned char*>(key);
3143-
pkey.reset(d2i_PrivateKey(EVP_PKEY_EC, nullptr, &p, key_len));
3164+
pkey->reset(d2i_PrivateKey(EVP_PKEY_EC, nullptr, &p, key_len));
31443165
}
31453166
}
31463167

31473168
// OpenSSL can fail to parse the key but still return a non-null pointer.
31483169
if (ERR_peek_error() != 0)
3149-
pkey.reset();
3170+
pkey->reset();
31503171

3151-
return pkey;
3172+
if (*pkey)
3173+
return ParseKeyResult::kParseKeyOk;
3174+
if (pc_info.CalledButEmpty())
3175+
return ParseKeyResult::kParseKeyNeedPassphrase;
3176+
return ParseKeyResult::kParseKeyFailed;
31523177
}
31533178

31543179
ByteSource::ByteSource(ByteSource&& other)
@@ -3284,6 +3309,25 @@ static PublicKeyEncodingConfig GetPublicKeyEncodingFromJs(
32843309
return result;
32853310
}
32863311

3312+
static inline ManagedEVPPKey GetParsedKey(Environment* env,
3313+
EVPKeyPointer&& pkey,
3314+
ParseKeyResult ret,
3315+
const char* default_msg) {
3316+
switch (ret) {
3317+
case ParseKeyResult::kParseKeyOk:
3318+
CHECK(pkey);
3319+
break;
3320+
case ParseKeyResult::kParseKeyNeedPassphrase:
3321+
THROW_ERR_MISSING_PASSPHRASE(env,
3322+
"Passphrase required for encrypted key");
3323+
break;
3324+
default:
3325+
ThrowCryptoError(env, ERR_get_error(), default_msg);
3326+
}
3327+
3328+
return ManagedEVPPKey(std::move(pkey));
3329+
}
3330+
32873331
static NonCopyableMaybe<PrivateKeyEncodingConfig> GetPrivateKeyEncodingFromJs(
32883332
const FunctionCallbackInfo<Value>& args,
32893333
unsigned int* offset,
@@ -3339,11 +3383,12 @@ static ManagedEVPPKey GetPrivateKeyFromJs(
33393383
GetPrivateKeyEncodingFromJs(args, offset, kKeyContextInput);
33403384
if (config.IsEmpty())
33413385
return ManagedEVPPKey();
3342-
EVPKeyPointer pkey =
3343-
ParsePrivateKey(config.Release(), key.get(), key.size());
3344-
if (!pkey)
3345-
ThrowCryptoError(env, ERR_get_error(), "Failed to read private key");
3346-
return ManagedEVPPKey(std::move(pkey));
3386+
3387+
EVPKeyPointer pkey;
3388+
ParseKeyResult ret =
3389+
ParsePrivateKey(&pkey, config.Release(), key.get(), key.size());
3390+
return GetParsedKey(env, std::move(pkey), ret,
3391+
"Failed to read private key");
33473392
} else {
33483393
CHECK(args[*offset]->IsObject() && allow_key_object);
33493394
KeyObject* key;
@@ -3364,15 +3409,16 @@ static ManagedEVPPKey GetPublicOrPrivateKeyFromJs(
33643409
GetPrivateKeyEncodingFromJs(args, offset, kKeyContextInput);
33653410
if (config_.IsEmpty())
33663411
return ManagedEVPPKey();
3412+
3413+
ParseKeyResult ret;
33673414
PrivateKeyEncodingConfig config = config_.Release();
33683415
EVPKeyPointer pkey;
33693416
if (config.format_ == kKeyFormatPEM) {
33703417
// For PEM, we can easily determine whether it is a public or private key
33713418
// by looking for the respective PEM tags.
3372-
ParsePublicKeyResult ret = ParsePublicKeyPEM(&pkey, data.get(),
3373-
data.size());
3374-
if (ret == ParsePublicKeyResult::kParsePublicNotRecognized) {
3375-
pkey = ParsePrivateKey(config, data.get(), data.size());
3419+
ret = ParsePublicKeyPEM(&pkey, data.get(), data.size());
3420+
if (ret == ParseKeyResult::kParseKeyNotRecognized) {
3421+
ret = ParsePrivateKey(&pkey, config, data.get(), data.size());
33763422
}
33773423
} else {
33783424
// For DER, the type determines how to parse it. SPKI, PKCS#8 and SEC1 are
@@ -3395,14 +3441,14 @@ static ManagedEVPPKey GetPublicOrPrivateKeyFromJs(
33953441
}
33963442

33973443
if (is_public) {
3398-
ParsePublicKey(&pkey, config, data.get(), data.size());
3444+
ret = ParsePublicKey(&pkey, config, data.get(), data.size());
33993445
} else {
3400-
pkey = ParsePrivateKey(config, data.get(), data.size());
3446+
ret = ParsePrivateKey(&pkey, config, data.get(), data.size());
34013447
}
34023448
}
3403-
if (!pkey)
3404-
ThrowCryptoError(env, ERR_get_error(), "Failed to read asymmetric key");
3405-
return ManagedEVPPKey(std::move(pkey));
3449+
3450+
return GetParsedKey(env, std::move(pkey), ret,
3451+
"Failed to read asymmetric key");
34063452
} else {
34073453
CHECK(args[*offset]->IsObject());
34083454
KeyObject* key = Unwrap<KeyObject>(args[*offset].As<Object>());
@@ -3585,6 +3631,7 @@ KeyType KeyObject::GetKeyType() const {
35853631
void KeyObject::Init(const FunctionCallbackInfo<Value>& args) {
35863632
KeyObject* key;
35873633
ASSIGN_OR_RETURN_UNWRAP(&key, args.Holder());
3634+
MarkPopErrorOnReturn mark_pop_error_on_return;
35883635

35893636
unsigned int offset;
35903637
ManagedEVPPKey pkey;
@@ -4780,6 +4827,8 @@ void Sign::SignFinal(const FunctionCallbackInfo<Value>& args) {
47804827
Sign* sign;
47814828
ASSIGN_OR_RETURN_UNWRAP(&sign, args.Holder());
47824829

4830+
ClearErrorOnReturn clear_error_on_return;
4831+
47834832
unsigned int offset = 0;
47844833
ManagedEVPPKey key = GetPrivateKeyFromJs(args, &offset, true);
47854834
if (!key)
@@ -4791,8 +4840,6 @@ void Sign::SignFinal(const FunctionCallbackInfo<Value>& args) {
47914840
CHECK(args[offset + 1]->IsInt32());
47924841
int salt_len = args[offset + 1].As<Int32>()->Value();
47934842

4794-
ClearErrorOnReturn clear_error_on_return;
4795-
47964843
SignResult ret = sign->SignFinal(
47974844
key,
47984845
padding,

‎src/node_errors.h

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ void FatalException(v8::Isolate* isolate,
5151
V(ERR_MEMORY_ALLOCATION_FAILED, Error) \
5252
V(ERR_MISSING_ARGS, TypeError) \
5353
V(ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST, TypeError) \
54+
V(ERR_MISSING_PASSPHRASE, TypeError) \
5455
V(ERR_MISSING_PLATFORM_FOR_WORKER, Error) \
5556
V(ERR_MODULE_NOT_FOUND, Error) \
5657
V(ERR_OUT_OF_RANGE, RangeError) \

‎test/fixtures/keys/Makefile

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ all: \
2626
dh2048.pem \
2727
dsa1025.pem \
2828
dsa_private_1025.pem \
29+
dsa_private_encrypted_1025.pem \
2930
dsa_public_1025.pem \
3031
ec-cert.pem \
3132
ec.pfx \
@@ -549,6 +550,9 @@ dsa1025.pem:
549550
dsa_private_1025.pem:
550551
openssl gendsa -out dsa_private_1025.pem dsa1025.pem
551552

553+
dsa_private_encrypted_1025.pem:
554+
openssl pkcs8 -in dsa_private_1025.pem -topk8 -passout 'pass:secret' -out dsa_private_encrypted_1025.pem
555+
552556
dsa_public_1025.pem:
553557
openssl dsa -in dsa_private_1025.pem -pubout -out dsa_public_1025.pem
554558

0 commit comments

Comments
 (0)
Please sign in to comment.