Skip to content

Commit

Permalink
test: add OpenSSL 3.x providers test
Browse files Browse the repository at this point in the history
Add basic tests for providers when using OpenSSL 3.x.

PR-URL: #44148
Reviewed-By: Michael Dawson <midawson@redhat.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
  • Loading branch information
richardlau committed Aug 24, 2022
1 parent 83cd484 commit 53ec358
Show file tree
Hide file tree
Showing 11 changed files with 303 additions and 0 deletions.
46 changes: 46 additions & 0 deletions test/addons/openssl-providers/binding.cc
@@ -0,0 +1,46 @@
#include <assert.h>
#include <node.h>
#include <openssl/provider.h>

namespace {

using v8::Array;
using v8::Context;
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;

#if OPENSSL_VERSION_MAJOR >= 3
int collectProviders(OSSL_PROVIDER* provider, void* cbdata) {
static_cast<std::vector<OSSL_PROVIDER*>*>(cbdata)->push_back(provider);
return 1;
}
#endif

inline void GetProviders(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
std::vector<Local<Value>> arr = {};
#if OPENSSL_VERSION_MAJOR >= 3
std::vector<OSSL_PROVIDER*> providers;
OSSL_PROVIDER_do_all(nullptr, &collectProviders, &providers);
for (auto provider : providers) {
arr.push_back(
String::NewFromUtf8(isolate, OSSL_PROVIDER_get0_name(provider))
.ToLocalChecked());
}
#endif
args.GetReturnValue().Set(Array::New(isolate, arr.data(), arr.size()));
}

inline void Initialize(Local<Object> exports,
Local<Value> module,
Local<Context> context) {
NODE_SET_METHOD(exports, "getProviders", GetProviders);
}

} // anonymous namespace

NODE_MODULE_CONTEXT_AWARE(NODE_GYP_MODULE_NAME, Initialize)
38 changes: 38 additions & 0 deletions test/addons/openssl-providers/binding.gyp
@@ -0,0 +1,38 @@
{
'targets': [
{
'target_name': 'binding',
'includes': ['../common.gypi'],
'conditions': [
['node_use_openssl=="true"', {
'conditions': [
['OS=="aix"', {
'variables': {
# Used to differentiate `AIX` and `OS400`(IBM i).
'aix_variant_name': '<!(uname -s)',
},
'conditions': [
[ '"<(aix_variant_name)"!="OS400"', { # Not `OS400`(IBM i)
'sources': ['binding.cc'],
'include_dirs': ['../../../deps/openssl/openssl/include'],
}],
],
}, {
'sources': ['binding.cc'],
'include_dirs': ['../../../deps/openssl/openssl/include'],
}],
],
}],
['OS=="mac"', {
'xcode_settings': {
'OTHER_CFLAGS+': [
'-Wno-deprecated-declarations',
],
},
}, {
'cflags': ['-Wno-deprecated-declarations'],
}],
],
},
]
}
73 changes: 73 additions & 0 deletions test/addons/openssl-providers/providers.cjs
@@ -0,0 +1,73 @@
'use strict';

const common = require('../../common');
if (!common.hasCrypto)
common.skip('missing crypto');

if (!common.hasOpenSSL3)
common.skip('this test requires OpenSSL 3.x');
const assert = require('node:assert');
const { createHash, getCiphers, getHashes } = require('node:crypto');
const { debuglog } = require('node:util');
const { getProviders } = require(`./build/${common.buildType}/binding`);

// For the providers defined here, the expectation is that the listed ciphers
// and hash algorithms are only provided by the named provider. These are for
// basic checks and are not intended to list evey cipher or hash algorithm
// supported by the provider.
const providers = {
'default': {
ciphers: ['des3-wrap'],
hashes: ['sha512-256'],
},
'legacy': {
ciphers: ['blowfish', 'idea'],
hashes: ['md4', 'whirlpool'],
},
};

const debug = debuglog('test');

module.exports = {
getCurrentProviders: getProviders,
testProviderPresent,
testProviderAbsent,
};

function assertArrayDoesNotInclude(array, item, desc) {
assert(!array.includes(item),
`${desc} [${array}] unexpectedly includes "${item}"`);
}

function assertArrayIncludes(array, item, desc) {
assert(array.includes(item),
`${desc} [${array}] does not include "${item}"`);
}

function testProviderPresent(provider) {
debug(`Checking '${provider}' is present`);
assertArrayIncludes(getProviders(), provider, 'Loaded providers');
for (const cipher of providers[provider].ciphers || []) {
debug(`Checking '${cipher}' cipher is available`);
assertArrayIncludes(getCiphers(), cipher, 'Available ciphers');
}
for (const hash of providers[provider].hashes || []) {
debug(`Checking '${hash}' hash is available`);
assertArrayIncludes(getHashes(), hash, 'Available hashes');
createHash(hash);
}
}

function testProviderAbsent(provider) {
debug(`Checking '${provider}' is absent`);
assertArrayDoesNotInclude(getProviders(), provider, 'Loaded providers');
for (const cipher of providers[provider].ciphers || []) {
debug(`Checking '${cipher}' cipher is unavailable`);
assertArrayDoesNotInclude(getCiphers(), cipher, 'Available ciphers');
}
for (const hash of providers[provider].hashes || []) {
debug(`Checking '${hash}' hash is unavailable`);
assertArrayDoesNotInclude(getHashes(), hash, 'Available hashes');
assert.throws(() => { createHash(hash); }, { code: 'ERR_OSSL_EVP_UNSUPPORTED' });
}
}
21 changes: 21 additions & 0 deletions test/addons/openssl-providers/test-default-only-config.js
@@ -0,0 +1,21 @@
'use strict';

const common = require('../../common');
const { path: fixture } = require('../../common/fixtures');
const providers = require('./providers.cjs');

const assert = require('node:assert');
const { fork } = require('node:child_process');
const option = `--openssl-config=${fixture('openssl3-conf', 'default_only.cnf')}`;

if (!process.execArgv.includes(option)) {
const cp = fork(__filename, { execArgv: [option] });
cp.on('exit', common.mustCall((code, signal) => {
assert.strictEqual(code, 0);
assert.strictEqual(signal, null);
}));
return;
}

providers.testProviderPresent('default');
providers.testProviderAbsent('legacy');
21 changes: 21 additions & 0 deletions test/addons/openssl-providers/test-legacy-provider-config.js
@@ -0,0 +1,21 @@
'use strict';

const common = require('../../common');
const { path: fixture } = require('../../common/fixtures');
const providers = require('./providers.cjs');

const assert = require('node:assert');
const { fork } = require('node:child_process');
const option = `--openssl-config=${fixture('openssl3-conf', 'legacy_provider_enabled.cnf')}`;

if (!process.execArgv.includes(option)) {
const cp = fork(__filename, { execArgv: [option] });
cp.on('exit', common.mustCall((code, signal) => {
assert.strictEqual(code, 0);
assert.strictEqual(signal, null);
}));
return;
}

providers.testProviderPresent('default');
providers.testProviderPresent('legacy');
@@ -0,0 +1,21 @@
'use strict';

const common = require('../../common');
const { path: fixture } = require('../../common/fixtures');
const providers = require('./providers.cjs');

const assert = require('node:assert');
const { fork } = require('node:child_process');
const option = `--openssl-config=${fixture('openssl3-conf', 'legacy_provider_inactive.cnf')}`;

if (!process.execArgv.includes(option)) {
const cp = fork(__filename, { execArgv: [option] });
cp.on('exit', common.mustCall((code, signal) => {
assert.strictEqual(code, 0);
assert.strictEqual(signal, null);
}));
return;
}

providers.testProviderPresent('default');
providers.testProviderAbsent('legacy');
24 changes: 24 additions & 0 deletions test/addons/openssl-providers/test-legacy-provider-option.js
@@ -0,0 +1,24 @@
'use strict';

const common = require('../../common');
const providers = require('./providers.cjs');
const assert = require('node:assert');
const { fork } = require('node:child_process');
const { getFips } = require('node:crypto');

const option = '--openssl-legacy-provider';
const execArgv = process.execArgv;
if (!execArgv.includes(option)) {
const cp = fork(__filename, { execArgv: [ option ] });
cp.on('exit', common.mustCall((code, signal) => {
assert.strictEqual(code, 0);
assert.strictEqual(signal, null);
}));
return;
}

// Enabling FIPS will make all legacy provider algorithms unavailable.
if (getFips()) {
common.skip('this test cannot be run in FIPS mode');
}
providers.testProviderPresent('legacy');
18 changes: 18 additions & 0 deletions test/addons/openssl-providers/test-no-legacy-provider-option.js
@@ -0,0 +1,18 @@
'use strict';

const common = require('../../common');
const providers = require('./providers.cjs');
const assert = require('node:assert');
const { fork } = require('node:child_process');

const option = '--no-openssl-legacy-provider';
if (!process.execArgv.includes(option)) {
const cp = fork(__filename, { execArgv: [ option ] });
cp.on('exit', common.mustCall((code, signal) => {
assert.strictEqual(code, 0);
assert.strictEqual(signal, null);
}));
return;
}

providers.testProviderAbsent('legacy');
11 changes: 11 additions & 0 deletions test/fixtures/openssl3-conf/default_only.cnf
@@ -0,0 +1,11 @@
nodejs_conf = nodejs_init

[nodejs_init]
providers = provider_sect

# List of providers to load
[provider_sect]
default = default_sect

[default_sect]
activate = 1
15 changes: 15 additions & 0 deletions test/fixtures/openssl3-conf/legacy_provider_enabled.cnf
@@ -0,0 +1,15 @@
nodejs_conf = nodejs_init

[nodejs_init]
providers = provider_sect

# List of providers to load
[provider_sect]
default = default_sect
legacy = legacy_sect

[default_sect]
activate = 1

[legacy_sect]
activate = 1
15 changes: 15 additions & 0 deletions test/fixtures/openssl3-conf/legacy_provider_inactive.cnf
@@ -0,0 +1,15 @@
nodejs_conf = nodejs_init

[nodejs_init]
providers = provider_sect

# List of providers to load
[provider_sect]
default = default_sect
legacy = legacy_sect

[default_sect]
activate = 1

[legacy_sect]
# 'activate' line intentionally omitted -- legacy provider should not be loaded.

0 comments on commit 53ec358

Please sign in to comment.