Skip to content

Commit 946c770

Browse files
committed
crypto: Add crypto.getEngines()
This adds a new api to show the list of loaded engines of OpenSSL. It also includes the test of dynamic engine for `crypto.setEngine()`.
1 parent ed12ea3 commit 946c770

File tree

9 files changed

+182
-0
lines changed

9 files changed

+182
-0
lines changed

doc/api/crypto.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,6 +1698,25 @@ is a bit field taking one of or a mix of the following flags (defined in
16981698
* `crypto.constants.ENGINE_METHOD_ALL`
16991699
* `crypto.constants.ENGINE_METHOD_NONE`
17001700

1701+
### crypto.getEngines()
1702+
<!-- YAML
1703+
added: REPLACEME
1704+
-->
1705+
1706+
Returns an array of objects with id, name and flags of loaded engines.
1707+
The flags value represents an integer of one of or a mix of the crypto
1708+
constants described above.
1709+
1710+
```js
1711+
const crypto = require('crypto');
1712+
console.log(crypto.getEngines());
1713+
// Prints:
1714+
// [ { id: 'rdrand', name: 'Intel RDRAND engine', flags: 8 },
1715+
// { id: 'dynamic',
1716+
// name: 'Dynamic engine loading support',
1717+
// flags: 4 } ]
1718+
```
1719+
17011720
### crypto.timingSafeEqual(a, b)
17021721
<!-- YAML
17031722
added: v6.6.0

lib/crypto.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,9 @@ exports.setEngine = function setEngine(id, flags) {
650650
return binding.setEngine(id, flags);
651651
};
652652

653+
654+
exports.getEngines = binding.getEngines;
655+
653656
exports.randomBytes = exports.pseudoRandomBytes = randomBytes;
654657

655658
exports.rng = exports.prng = randomBytes;

node.gyp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,18 @@
691691
'ldflags': [ '-I<(SHARED_INTERMEDIATE_DIR)' ]
692692
}],
693693
]
694+
},
695+
{
696+
# node test engine used in test/parallel/test-crypto-engine.js
697+
'target_name': 'node_test_engine',
698+
'type': 'loadable_module',
699+
'conditions': [
700+
['node_use_openssl=="true" and '
701+
'node_shared_openssl=="false" and '
702+
'openssl_fips==""', { # fipsld fails to link libcrypto.a
703+
'includes': ['test/fixtures/openssl_test_engine/node_test_engine.gypi'],
704+
}],
705+
],
694706
}
695707
], # end targets
696708

src/env.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ namespace node {
104104
V(emitting_top_level_domain_error_string, "_emittingTopLevelDomainError") \
105105
V(exchange_string, "exchange") \
106106
V(enumerable_string, "enumerable") \
107+
V(id_string, "id") \
107108
V(idle_string, "idle") \
108109
V(irq_string, "irq") \
109110
V(encoding_string, "encoding") \

src/node_crypto.cc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5988,11 +5988,41 @@ void SetEngine(const FunctionCallbackInfo<Value>& args) {
59885988
}
59895989
}
59905990

5991+
ENGINE_set_flags(engine, flags);
59915992
int r = ENGINE_set_default(engine, flags);
59925993
ENGINE_free(engine);
59935994
if (r == 0)
59945995
return ThrowCryptoError(env, ERR_get_error());
59955996
}
5997+
5998+
5999+
void GetEngines(const FunctionCallbackInfo<Value>& args) {
6000+
Environment* env = Environment::GetCurrent(args);
6001+
ENGINE* e;
6002+
int i = 0;
6003+
Local<Array> arr = Array::New(env->isolate());
6004+
6005+
for (e = ENGINE_get_first(); e != nullptr; e = ENGINE_get_next(e)) {
6006+
const char* id = ENGINE_get_id(e);
6007+
const char* name = ENGINE_get_name(e);
6008+
int flags = ENGINE_get_flags(e);
6009+
6010+
if (id == nullptr || name == nullptr) {
6011+
ENGINE_free(e);
6012+
return env->ThrowError("Invalid Engine: id or name is not found.");
6013+
}
6014+
6015+
Local<Object> obj = Object::New(env->isolate());
6016+
obj->Set(env->id_string(), OneByteString(env->isolate(), id));
6017+
obj->Set(env->name_string(), OneByteString(env->isolate(), name));
6018+
obj->Set(env->flags_string(), Integer::New(env->isolate(), flags));
6019+
arr->Set(i++, obj);
6020+
}
6021+
6022+
ENGINE_free(e);
6023+
6024+
args.GetReturnValue().Set(arr);
6025+
}
59966026
#endif // !OPENSSL_NO_ENGINE
59976027

59986028
void GetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
@@ -6042,6 +6072,7 @@ void InitCrypto(Local<Object> target,
60426072
env->SetMethod(target, "certExportChallenge", ExportChallenge);
60436073
#ifndef OPENSSL_NO_ENGINE
60446074
env->SetMethod(target, "setEngine", SetEngine);
6075+
env->SetMethod(target, "getEngines", GetEngines);
60456076
#endif // !OPENSSL_NO_ENGINE
60466077
env->SetMethod(target, "getFipsCrypto", GetFipsCrypto);
60476078
env->SetMethod(target, "setFipsCrypto", SetFipsCrypto);

src/node_crypto.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,7 @@ class ECDH : public BaseObject {
744744
bool EntropySource(unsigned char* buffer, size_t length);
745745
#ifndef OPENSSL_NO_ENGINE
746746
void SetEngine(const v8::FunctionCallbackInfo<v8::Value>& args);
747+
void GetEngines(const v8::FunctionCallbackInfo<v8::Value>& args);
747748
#endif // !OPENSSL_NO_ENGINE
748749
void InitCrypto(v8::Local<v8::Object> target);
749750

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <openssl/engine.h>
2+
3+
static const char *engine_id = "node_test_engine";
4+
static const char *engine_name = "Node Test Engine for OpenSSL";
5+
6+
static int bind(ENGINE *e, const char *id)
7+
{
8+
ENGINE_set_id(e, engine_id);
9+
ENGINE_set_name(e, engine_name);
10+
return 1;
11+
}
12+
13+
IMPLEMENT_DYNAMIC_BIND_FN(bind)
14+
IMPLEMENT_DYNAMIC_CHECK_FN()
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
'sources': ['node_test_engine.c'],
3+
'conditions': [
4+
['OS=="mac"', {
5+
'include_dirs': ['<(PRODUCT_DIR)/../../deps/openssl/openssl/include',],
6+
'library_dirs': ['<(LIB_DIR)'],
7+
'libraries': ['-lopenssl'],
8+
}, 'OS=="win"', {
9+
'dependencies': [
10+
'./deps/openssl/openssl.gyp:openssl',
11+
],
12+
'include_dirs': ['<(PRODUCT_DIR)/../deps/openssl/openssl/include',],
13+
'library_dirs': ['<(LIB_DIR)'],
14+
'libraries': [
15+
'-lkernel32.lib',
16+
'-luser32.lib',
17+
'-lgdi32.lib',
18+
'-lwinspool.lib',
19+
'-lcomdlg32.lib',
20+
'-ladvapi32.lib',
21+
'-lshell32.lib',
22+
'-lole32.lib',
23+
'-loleaut32.lib',
24+
'-luuid.lib',
25+
'-lodbc32.lib',
26+
'-lDelayImp.lib',
27+
'-lopenssl.lib',
28+
],
29+
}, {
30+
'library_dirs': ['<(LIB_DIR)/deps/openssl'],
31+
'ldflags': ['-lopenssl'],
32+
'include_dirs': ['<(PRODUCT_DIR)/../../deps/openssl/openssl/include',],
33+
}],
34+
[ 'OS in "freebsd openbsd netbsd solaris" or \
35+
(OS=="linux" and target_arch!="ia32")', {
36+
'cflags': ['-fPIC'],
37+
}],
38+
],
39+
}

test/parallel/test-crypto-engine.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,76 @@
11
'use strict';
22
const common = require('../common');
33

4+
// This test ensures that a dynamic engine can be registered with
5+
// crypto.setEngine() and crypto.getEngines() obtains the list for
6+
// both builtin and dynamic engines.
7+
48
if (!common.hasCrypto) {
59
common.skip('missing crypto');
610
return;
711
}
812

913
const assert = require('assert');
1014
const crypto = require('crypto');
15+
const fs = require('fs');
16+
const path = require('path');
17+
18+
let found = 0;
19+
20+
// check builtin engine exists.
21+
// A dynamic engine is loaded by ENGINE_load_builtin_engines()
22+
// in InitCryptoOnce().
23+
crypto.getEngines().forEach((e) => {
24+
if (e.id === 'dynamic')
25+
found++;
26+
});
27+
28+
assert.strictEqual(found, 1);
29+
30+
// check set and get node test engine of
31+
// test/fixtures/openssl_test_engine/node_test_engine.c
32+
33+
if (process.config.variables.node_shared_openssl) {
34+
common.skip('node test engine cannot be built in shared openssl');
35+
return;
36+
}
37+
38+
if (process.config.variables.openssl_fips) {
39+
common.skip('node test engine cannot be built in FIPS mode.');
40+
return;
41+
}
42+
43+
let engine_lib;
44+
45+
if (common.isWindows) {
46+
engine_lib = 'node_test_engine.dll';
47+
} else if (common.isAix) {
48+
engine_lib = 'libnode_test_engine.a';
49+
} else if (process.platform === 'darwin') {
50+
engine_lib = 'node_test_engine.so';
51+
} else {
52+
engine_lib = 'libnode_test_engine.so';
53+
}
54+
55+
const test_engine_id = 'node_test_engine';
56+
const test_engine_name = 'Node Test Engine for OpenSSL';
57+
const test_engine = path.join(path.dirname(process.execPath), engine_lib);
58+
59+
assert.doesNotThrow(function() {
60+
fs.accessSync(test_engine);
61+
}, 'node test engine ' + test_engine + ' is not found.');
62+
63+
crypto.setEngine(test_engine);
64+
65+
crypto.getEngines().forEach((e) => {
66+
if (e.id === test_engine_id && e.name === test_engine_name &&
67+
e.flags === crypto.constants.ENGINE_METHOD_ALL)
68+
found++;
69+
});
70+
71+
assert.strictEqual(found, 2);
1172

73+
// Error Tests for setEngine
1274
assert.throws(function() {
1375
crypto.setEngine(true);
1476
}, /^TypeError: "id" argument should be a string$/);

0 commit comments

Comments
 (0)