Skip to content

Commit 4c446ad

Browse files
committed
src: move cipher list iteration to ncrypto
1 parent f20135d commit 4c446ad

4 files changed

Lines changed: 98 additions & 73 deletions

File tree

deps/ncrypto/ncrypto.cc

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3717,6 +3717,76 @@ DataPointer Cipher::recover(const EVPKeyPointer& key,
37173717
key, params, in);
37183718
}
37193719

3720+
namespace {
3721+
struct CipherCallbackContext {
3722+
Cipher::CipherNameCallback cb;
3723+
void operator()(std::string_view name) {
3724+
cb(name);
3725+
}
3726+
};
3727+
3728+
#if OPENSSL_VERSION_MAJOR >= 3
3729+
template <class TypeName,
3730+
TypeName* fetch_type(OSSL_LIB_CTX*, const char*, const char*),
3731+
void free_type(TypeName*),
3732+
const TypeName* getbyname(const char*),
3733+
const char* getname(const TypeName*)>
3734+
void array_push_back(const TypeName* evp_ref,
3735+
const char* from,
3736+
const char* to,
3737+
void* arg) {
3738+
if (from == nullptr) return;
3739+
3740+
const TypeName* real_instance = getbyname(from);
3741+
if (!real_instance) return;
3742+
3743+
const char* real_name = getname(real_instance);
3744+
if (!real_name) return;
3745+
3746+
// EVP_*_fetch() does not support alias names, so we need to pass it the
3747+
// real/original algorithm name.
3748+
// We use EVP_*_fetch() as a filter here because it will only return an
3749+
// instance if the algorithm is supported by the public OpenSSL APIs (some
3750+
// algorithms are used internally by OpenSSL and are also passed to this
3751+
// callback).
3752+
TypeName* fetched = fetch_type(nullptr, real_name, nullptr);
3753+
if (fetched == nullptr) return;
3754+
3755+
free_type(fetched);
3756+
auto& cb = *(static_cast<CipherCallbackContext*>(arg));
3757+
cb(from);
3758+
}
3759+
#else
3760+
template <class TypeName>
3761+
void array_push_back(const TypeName* evp_ref,
3762+
const char* from,
3763+
const char* to,
3764+
void* arg) {
3765+
if (!from) return;
3766+
auto& cb = *(static_cast<CipherCallbackContext*>(arg));
3767+
cb(from);
3768+
}
3769+
#endif
3770+
} // namespace
3771+
3772+
void Cipher::ForEach(Cipher::CipherNameCallback callback) {
3773+
ClearErrorOnReturn clearErrorOnReturn;
3774+
CipherCallbackContext context;
3775+
context.cb = std::move(callback);
3776+
3777+
EVP_CIPHER_do_all_sorted(
3778+
#if OPENSSL_VERSION_MAJOR >= 3
3779+
array_push_back<EVP_CIPHER,
3780+
EVP_CIPHER_fetch,
3781+
EVP_CIPHER_free,
3782+
EVP_get_cipherbyname,
3783+
EVP_CIPHER_get0_name>,
3784+
#else
3785+
array_push_back<EVP_CIPHER>,
3786+
#endif
3787+
&context);
3788+
}
3789+
37203790
// ============================================================================
37213791

37223792
Ec::Ec() : ec_(nullptr) {}

deps/ncrypto/ncrypto.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,12 @@ class Cipher final {
287287
static const Cipher FromNid(int nid);
288288
static const Cipher FromCtx(const CipherCtxPointer& ctx);
289289

290+
using CipherNameCallback = std::function<void(std::string_view name)>;
291+
292+
// Iterates the known ciphers if the underlying implementation
293+
// is able to do so.
294+
static void ForEach(CipherNameCallback callback);
295+
290296
// Utilities to get various ciphers by type. If the underlying
291297
// implementation does not support the requested cipher, then
292298
// the result will be an empty Cipher object whose bool operator

src/crypto/crypto_cipher.cc

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -182,20 +182,28 @@ void CipherBase::GetSSLCiphers(const FunctionCallbackInfo<Value>& args) {
182182

183183
void CipherBase::GetCiphers(const FunctionCallbackInfo<Value>& args) {
184184
Environment* env = Environment::GetCurrent(args);
185-
MarkPopErrorOnReturn mark_pop_error_on_return;
186-
CipherPushContext ctx(env);
187-
EVP_CIPHER_do_all_sorted(
188-
#if OPENSSL_VERSION_MAJOR >= 3
189-
array_push_back<EVP_CIPHER,
190-
EVP_CIPHER_fetch,
191-
EVP_CIPHER_free,
192-
EVP_get_cipherbyname,
193-
EVP_CIPHER_get0_name>,
194-
#else
195-
array_push_back<EVP_CIPHER>,
196-
#endif
197-
&ctx);
198-
args.GetReturnValue().Set(ctx.ToJSArray());
185+
LocalVector<Value> ciphers(env->isolate());
186+
bool errored = false;
187+
Cipher::ForEach([&](std::string_view name) {
188+
// If a prior iteration errored, do nothing further. We apparently
189+
// can't actually stop openssl from stopping its iteration here.
190+
// But why does it matter? Good question.
191+
if (errored) return;
192+
Local<Value> val;
193+
if (!ToV8Value(env->context(), name, env->isolate()).ToLocal(&val)) {
194+
errored = true;
195+
return;
196+
}
197+
ciphers.push_back(val);
198+
});
199+
200+
// If errored is true here, then we encountered a JavaScript error
201+
// while trying to create the V8 String from the std::string_view
202+
// in the iteration callback. That means we need to throw.
203+
if (!errored) {
204+
args.GetReturnValue().Set(
205+
Array::New(env->isolate(), ciphers.data(), ciphers.size()));
206+
}
199207
}
200208

201209
CipherBase::CipherBase(Environment* env,

src/crypto/crypto_util.h

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -479,65 +479,6 @@ void ThrowCryptoError(Environment* env,
479479
unsigned long err, // NOLINT(runtime/int)
480480
const char* message = nullptr);
481481

482-
class CipherPushContext final {
483-
public:
484-
inline explicit CipherPushContext(Environment* env)
485-
: list_(env->isolate()), env_(env) {}
486-
487-
inline void push_back(const char* str) {
488-
list_.emplace_back(OneByteString(env_->isolate(), str));
489-
}
490-
491-
inline v8::Local<v8::Array> ToJSArray() {
492-
return v8::Array::New(env_->isolate(), list_.data(), list_.size());
493-
}
494-
495-
private:
496-
v8::LocalVector<v8::Value> list_;
497-
Environment* env_;
498-
};
499-
500-
#if OPENSSL_VERSION_MAJOR >= 3
501-
template <class TypeName,
502-
TypeName* fetch_type(OSSL_LIB_CTX*, const char*, const char*),
503-
void free_type(TypeName*),
504-
const TypeName* getbyname(const char*),
505-
const char* getname(const TypeName*)>
506-
void array_push_back(const TypeName* evp_ref,
507-
const char* from,
508-
const char* to,
509-
void* arg) {
510-
if (!from) return;
511-
512-
const TypeName* real_instance = getbyname(from);
513-
if (!real_instance) return;
514-
515-
const char* real_name = getname(real_instance);
516-
if (!real_name) return;
517-
518-
// EVP_*_fetch() does not support alias names, so we need to pass it the
519-
// real/original algorithm name.
520-
// We use EVP_*_fetch() as a filter here because it will only return an
521-
// instance if the algorithm is supported by the public OpenSSL APIs (some
522-
// algorithms are used internally by OpenSSL and are also passed to this
523-
// callback).
524-
TypeName* fetched = fetch_type(nullptr, real_name, nullptr);
525-
if (!fetched) return;
526-
527-
free_type(fetched);
528-
static_cast<CipherPushContext*>(arg)->push_back(from);
529-
}
530-
#else
531-
template <class TypeName>
532-
void array_push_back(const TypeName* evp_ref,
533-
const char* from,
534-
const char* to,
535-
void* arg) {
536-
if (!from) return;
537-
static_cast<CipherPushContext*>(arg)->push_back(from);
538-
}
539-
#endif
540-
541482
// WebIDL AllowSharedBufferSource.
542483
inline bool IsAnyBufferSource(v8::Local<v8::Value> arg) {
543484
return arg->IsArrayBufferView() ||

0 commit comments

Comments
 (0)