-
-
Notifications
You must be signed in to change notification settings - Fork 35.4k
crypto: Add crypto.getEngines() #10865
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5988,11 +5988,41 @@ void SetEngine(const FunctionCallbackInfo<Value>& args) { | |
| } | ||
| } | ||
|
|
||
| ENGINE_set_flags(engine, flags); | ||
| int r = ENGINE_set_default(engine, flags); | ||
| ENGINE_free(engine); | ||
| if (r == 0) | ||
| return ThrowCryptoError(env, ERR_get_error()); | ||
| } | ||
|
|
||
|
|
||
| void GetEngines(const FunctionCallbackInfo<Value>& args) { | ||
| Environment* env = Environment::GetCurrent(args); | ||
| ENGINE* e; | ||
| int i = 0; | ||
| Local<Array> arr = Array::New(env->isolate()); | ||
|
|
||
| for (e = ENGINE_get_first(); e != nullptr; e = ENGINE_get_next(e)) { | ||
| const char* id = ENGINE_get_id(e); | ||
| const char* name = ENGINE_get_name(e); | ||
| int flags = ENGINE_get_flags(e); | ||
|
|
||
| if (id == nullptr || name == nullptr) { | ||
| ENGINE_free(e); | ||
| return env->ThrowError("Invalid Engine: id or name is not found."); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if this is the right approach. If there is an engine with an id, but no name, then this will throw, but the user won't know what engine it is that has a name, it could be very frustrating. What about just using |
||
| } | ||
|
|
||
| Local<Object> obj = Object::New(env->isolate()); | ||
| obj->Set(env->id_string(), OneByteString(env->isolate(), id)); | ||
| obj->Set(env->name_string(), OneByteString(env->isolate(), name)); | ||
| obj->Set(env->flags_string(), Integer::New(env->isolate(), flags)); | ||
| arr->Set(i++, obj); | ||
| } | ||
|
|
||
| ENGINE_free(e); | ||
|
|
||
| args.GetReturnValue().Set(arr); | ||
| } | ||
| #endif // !OPENSSL_NO_ENGINE | ||
|
|
||
| void GetFipsCrypto(const FunctionCallbackInfo<Value>& args) { | ||
|
|
@@ -6042,6 +6072,7 @@ void InitCrypto(Local<Object> target, | |
| env->SetMethod(target, "certExportChallenge", ExportChallenge); | ||
| #ifndef OPENSSL_NO_ENGINE | ||
| env->SetMethod(target, "setEngine", SetEngine); | ||
| env->SetMethod(target, "getEngines", GetEngines); | ||
| #endif // !OPENSSL_NO_ENGINE | ||
| env->SetMethod(target, "getFipsCrypto", GetFipsCrypto); | ||
| env->SetMethod(target, "setFipsCrypto", SetFipsCrypto); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| #include <openssl/engine.h> | ||
|
|
||
| static const char *engine_id = "node_test_engine"; | ||
| static const char *engine_name = "Node Test Engine for OpenSSL"; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tiny style nit: star leans left ( static const char engine_id[] = ...;Saves a pointer indirection. |
||
|
|
||
| static int bind(ENGINE *e, const char *id) | ||
| { | ||
| ENGINE_set_id(e, engine_id); | ||
| ENGINE_set_name(e, engine_name); | ||
| return 1; | ||
| } | ||
|
|
||
| IMPLEMENT_DYNAMIC_BIND_FN(bind) | ||
| IMPLEMENT_DYNAMIC_CHECK_FN() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| { | ||
| 'sources': ['node_test_engine.c'], | ||
| 'conditions': [ | ||
| ['OS=="mac"', { | ||
| 'include_dirs': ['<(PRODUCT_DIR)/../../deps/openssl/openssl/include',], | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Superfluous comma, also in the other include_dirs lists. |
||
| 'library_dirs': ['<(LIB_DIR)'], | ||
| 'libraries': ['-lopenssl'], | ||
| }, 'OS=="win"', { | ||
| 'dependencies': [ | ||
| './deps/openssl/openssl.gyp:openssl', | ||
| ], | ||
| 'include_dirs': ['<(PRODUCT_DIR)/../deps/openssl/openssl/include',], | ||
| 'library_dirs': ['<(LIB_DIR)'], | ||
| 'libraries': [ | ||
| '-lkernel32.lib', | ||
| '-luser32.lib', | ||
| '-lgdi32.lib', | ||
| '-lwinspool.lib', | ||
| '-lcomdlg32.lib', | ||
| '-ladvapi32.lib', | ||
| '-lshell32.lib', | ||
| '-lole32.lib', | ||
| '-loleaut32.lib', | ||
| '-luuid.lib', | ||
| '-lodbc32.lib', | ||
| '-lDelayImp.lib', | ||
| '-lopenssl.lib', | ||
| ], | ||
| }, { | ||
| 'library_dirs': ['<(LIB_DIR)/deps/openssl'], | ||
| 'ldflags': ['-lopenssl'], | ||
| 'include_dirs': ['<(PRODUCT_DIR)/../../deps/openssl/openssl/include',], | ||
| }], | ||
| [ 'OS in "freebsd openbsd netbsd solaris" or \ | ||
| (OS=="linux" and target_arch!="ia32")', { | ||
| 'cflags': ['-fPIC'], | ||
| }], | ||
| ], | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,76 @@ | ||
| 'use strict'; | ||
| const common = require('../common'); | ||
|
|
||
| // This test ensures that a dynamic engine can be registered with | ||
| // crypto.setEngine() and crypto.getEngines() obtains the list for | ||
| // both builtin and dynamic engines. | ||
|
|
||
| if (!common.hasCrypto) { | ||
| common.skip('missing crypto'); | ||
| return; | ||
| } | ||
|
|
||
| const assert = require('assert'); | ||
| const crypto = require('crypto'); | ||
| const fs = require('fs'); | ||
| const path = require('path'); | ||
|
|
||
| let found = 0; | ||
|
|
||
| // check builtin engine exists. | ||
| // A dynamic engine is loaded by ENGINE_load_builtin_engines() | ||
| // in InitCryptoOnce(). | ||
| crypto.getEngines().forEach((e) => { | ||
| if (e.id === 'dynamic') | ||
| found++; | ||
| }); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. by reimplementing
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You mean found += crypto.getEngines().filter(e => e.id === 'dynamic').length? |
||
|
|
||
| assert.strictEqual(found, 1); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. assert conflicts with comment about "at least one"
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The description of comment was wrong. Fixed. |
||
|
|
||
| // check set and get node test engine of | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. s/check/Check/ |
||
| // test/fixtures/openssl_test_engine/node_test_engine.c | ||
|
|
||
| if (process.config.variables.node_shared_openssl) { | ||
| common.skip('node test engine cannot be built in shared openssl'); | ||
| return; | ||
| } | ||
|
|
||
| if (process.config.variables.openssl_fips) { | ||
| common.skip('node test engine cannot be built in FIPS mode.'); | ||
| return; | ||
| } | ||
|
|
||
| let engine_lib; | ||
|
|
||
| if (common.isWindows) { | ||
| engine_lib = 'node_test_engine.dll'; | ||
| } else if (common.isAix) { | ||
| engine_lib = 'libnode_test_engine.a'; | ||
| } else if (process.platform === 'darwin') { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| engine_lib = 'node_test_engine.so'; | ||
| } else { | ||
| engine_lib = 'libnode_test_engine.so'; | ||
| } | ||
|
|
||
| const test_engine_id = 'node_test_engine'; | ||
| const test_engine_name = 'Node Test Engine for OpenSSL'; | ||
| const test_engine = path.join(path.dirname(process.execPath), engine_lib); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tiny style nit: JS code uses camelCase, not snake_case (for the most part.) |
||
|
|
||
| assert.doesNotThrow(function() { | ||
| fs.accessSync(test_engine); | ||
| }, 'node test engine ' + test_engine + ' is not found.'); | ||
|
|
||
| crypto.setEngine(test_engine); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe call twice to prove its possible?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is possible. Do we need a such kind of combination tests for setEngine?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tests should test invalid behaviours, and obscure corner conditions. If it is allowed to call multiple times, the test should do, and assert on the result, IMO. It would be possible that the second call always fails, for example, or someone might believe that both "RSA" engines get added (which isn't true). The docs don't actually say what happens when setEngine() is called multiple times with the same flag. The latest call will replace the previous default, but its not stated. |
||
|
|
||
| crypto.getEngines().forEach((e) => { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any reason to use filter rather than forEach? It seems to be equivalent for me.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Its true that many of the Array methods can be implemented in terms of forEach(), so they are equivalent in that sense. Someone has to read this code, and understand that it is filtering. Better to just call |
||
| if (e.id === test_engine_id && e.name === test_engine_name && | ||
| e.flags === crypto.constants.ENGINE_METHOD_ALL) | ||
| found++; | ||
| }); | ||
|
|
||
| assert.strictEqual(found, 2); | ||
|
|
||
| // Error Tests for setEngine | ||
| assert.throws(function() { | ||
| crypto.setEngine(true); | ||
| }, /^TypeError: "id" argument should be a string$/); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
too bad there is no
ENGINE_get_flags()so we can know the flagsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch.
SetEnginedid not haveENGINE_set_flagso that flags was always 0 for dynamic engines. Fixed and add a new flags property.