mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 12:20:27 +01:00
crypto: add optional callback to crypto.diffieHellman
PR-URL: https://github.com/nodejs/node/pull/57274 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
This commit is contained in:
parent
2affc3ada7
commit
824c6a55f0
|
|
@ -3533,23 +3533,32 @@ the corresponding digest algorithm. This does not work for all signature
|
||||||
algorithms, such as `'ecdsa-with-SHA256'`, so it is best to always use digest
|
algorithms, such as `'ecdsa-with-SHA256'`, so it is best to always use digest
|
||||||
algorithm names.
|
algorithm names.
|
||||||
|
|
||||||
### `crypto.diffieHellman(options)`
|
### `crypto.diffieHellman(options[, callback])`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added:
|
added:
|
||||||
- v13.9.0
|
- v13.9.0
|
||||||
- v12.17.0
|
- v12.17.0
|
||||||
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/57274
|
||||||
|
description: Optional callback argument added.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
* `options`: {Object}
|
* `options`: {Object}
|
||||||
* `privateKey`: {KeyObject}
|
* `privateKey`: {KeyObject}
|
||||||
* `publicKey`: {KeyObject}
|
* `publicKey`: {KeyObject}
|
||||||
* Returns: {Buffer}
|
* `callback` {Function}
|
||||||
|
* `err` {Error}
|
||||||
|
* `secret` {Buffer}
|
||||||
|
* Returns: {Buffer} if the `callback` function is not provided.
|
||||||
|
|
||||||
Computes the Diffie-Hellman secret based on a `privateKey` and a `publicKey`.
|
Computes the Diffie-Hellman secret based on a `privateKey` and a `publicKey`.
|
||||||
Both keys must have the same `asymmetricKeyType`, which must be one of `'dh'`
|
Both keys must have the same `asymmetricKeyType`, which must be one of `'dh'`
|
||||||
(for Diffie-Hellman), `'ec'`, `'x448'`, or `'x25519'` (for ECDH).
|
(for Diffie-Hellman), `'ec'`, `'x448'`, or `'x25519'` (for ECDH).
|
||||||
|
|
||||||
|
If the `callback` function is provided this function uses libuv's threadpool.
|
||||||
|
|
||||||
### `crypto.fips`
|
### `crypto.fips`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
const {
|
const {
|
||||||
ArrayBufferPrototypeSlice,
|
ArrayBufferPrototypeSlice,
|
||||||
|
FunctionPrototypeCall,
|
||||||
MathCeil,
|
MathCeil,
|
||||||
ObjectDefineProperty,
|
ObjectDefineProperty,
|
||||||
SafeSet,
|
SafeSet,
|
||||||
|
|
@ -11,13 +12,14 @@ const {
|
||||||
const { Buffer } = require('buffer');
|
const { Buffer } = require('buffer');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
DHBitsJob,
|
||||||
DiffieHellman: _DiffieHellman,
|
DiffieHellman: _DiffieHellman,
|
||||||
DiffieHellmanGroup: _DiffieHellmanGroup,
|
DiffieHellmanGroup: _DiffieHellmanGroup,
|
||||||
ECDH: _ECDH,
|
ECDH: _ECDH,
|
||||||
ECDHBitsJob,
|
ECDHBitsJob,
|
||||||
ECDHConvertKey: _ECDHConvertKey,
|
ECDHConvertKey: _ECDHConvertKey,
|
||||||
statelessDH,
|
|
||||||
kCryptoJobAsync,
|
kCryptoJobAsync,
|
||||||
|
kCryptoJobSync,
|
||||||
} = internalBinding('crypto');
|
} = internalBinding('crypto');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
@ -32,6 +34,7 @@ const {
|
||||||
} = require('internal/errors');
|
} = require('internal/errors');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
validateFunction,
|
||||||
validateInt32,
|
validateInt32,
|
||||||
validateObject,
|
validateObject,
|
||||||
validateString,
|
validateString,
|
||||||
|
|
@ -268,9 +271,12 @@ function getFormat(format) {
|
||||||
|
|
||||||
const dhEnabledKeyTypes = new SafeSet(['dh', 'ec', 'x448', 'x25519']);
|
const dhEnabledKeyTypes = new SafeSet(['dh', 'ec', 'x448', 'x25519']);
|
||||||
|
|
||||||
function diffieHellman(options) {
|
function diffieHellman(options, callback) {
|
||||||
validateObject(options, 'options');
|
validateObject(options, 'options');
|
||||||
|
|
||||||
|
if (callback !== undefined)
|
||||||
|
validateFunction(callback, 'callback');
|
||||||
|
|
||||||
const { privateKey, publicKey } = options;
|
const { privateKey, publicKey } = options;
|
||||||
if (!(privateKey instanceof KeyObject))
|
if (!(privateKey instanceof KeyObject))
|
||||||
throw new ERR_INVALID_ARG_VALUE('options.privateKey', privateKey);
|
throw new ERR_INVALID_ARG_VALUE('options.privateKey', privateKey);
|
||||||
|
|
@ -293,7 +299,24 @@ function diffieHellman(options) {
|
||||||
`${privateType} and ${publicType}`);
|
`${privateType} and ${publicType}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return statelessDH(privateKey[kHandle], publicKey[kHandle]);
|
const job = new DHBitsJob(
|
||||||
|
callback ? kCryptoJobAsync : kCryptoJobSync,
|
||||||
|
publicKey[kHandle],
|
||||||
|
privateKey[kHandle]);
|
||||||
|
|
||||||
|
if (!callback) {
|
||||||
|
const { 0: err, 1: secret } = job.run();
|
||||||
|
if (err !== undefined)
|
||||||
|
throw err;
|
||||||
|
|
||||||
|
return Buffer.from(secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
job.ondone = (error, secret) => {
|
||||||
|
if (error) return FunctionPrototypeCall(callback, job, error);
|
||||||
|
FunctionPrototypeCall(callback, job, null, Buffer.from(secret));
|
||||||
|
};
|
||||||
|
job.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
let masks;
|
let masks;
|
||||||
|
|
|
||||||
|
|
@ -483,49 +483,11 @@ WebCryptoKeyExportStatus DHKeyExportTraits::DoExport(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
ByteSource StatelessDiffieHellmanThreadsafe(const EVPKeyPointer& our_key,
|
|
||||||
const EVPKeyPointer& their_key) {
|
|
||||||
auto dp = DHPointer::stateless(our_key, their_key);
|
|
||||||
if (!dp) return {};
|
|
||||||
DCHECK(!dp.isSecure());
|
|
||||||
|
|
||||||
return ByteSource::Allocated(dp.release());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stateless(const FunctionCallbackInfo<Value>& args) {
|
|
||||||
Environment* env = Environment::GetCurrent(args);
|
|
||||||
|
|
||||||
CHECK(args[0]->IsObject() && args[1]->IsObject());
|
|
||||||
KeyObjectHandle* our_key_object;
|
|
||||||
ASSIGN_OR_RETURN_UNWRAP(&our_key_object, args[0].As<Object>());
|
|
||||||
CHECK_EQ(our_key_object->Data().GetKeyType(), kKeyTypePrivate);
|
|
||||||
KeyObjectHandle* their_key_object;
|
|
||||||
ASSIGN_OR_RETURN_UNWRAP(&their_key_object, args[1].As<Object>());
|
|
||||||
CHECK_NE(their_key_object->Data().GetKeyType(), kKeyTypeSecret);
|
|
||||||
|
|
||||||
const auto& our_key = our_key_object->Data().GetAsymmetricKey();
|
|
||||||
const auto& their_key = their_key_object->Data().GetAsymmetricKey();
|
|
||||||
|
|
||||||
Local<Value> out;
|
|
||||||
if (!StatelessDiffieHellmanThreadsafe(our_key, their_key)
|
|
||||||
.ToBuffer(env)
|
|
||||||
.ToLocal(&out)) return;
|
|
||||||
|
|
||||||
if (Buffer::Length(out) == 0)
|
|
||||||
return ThrowCryptoError(env, ERR_get_error(), "diffieHellman failed");
|
|
||||||
|
|
||||||
args.GetReturnValue().Set(out);
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
Maybe<void> DHBitsTraits::AdditionalConfig(
|
Maybe<void> DHBitsTraits::AdditionalConfig(
|
||||||
CryptoJobMode mode,
|
CryptoJobMode mode,
|
||||||
const FunctionCallbackInfo<Value>& args,
|
const FunctionCallbackInfo<Value>& args,
|
||||||
unsigned int offset,
|
unsigned int offset,
|
||||||
DHBitsConfig* params) {
|
DHBitsConfig* params) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
|
||||||
|
|
||||||
CHECK(args[offset]->IsObject()); // public key
|
CHECK(args[offset]->IsObject()); // public key
|
||||||
CHECK(args[offset + 1]->IsObject()); // private key
|
CHECK(args[offset + 1]->IsObject()); // private key
|
||||||
|
|
||||||
|
|
@ -535,11 +497,8 @@ Maybe<void> DHBitsTraits::AdditionalConfig(
|
||||||
ASSIGN_OR_RETURN_UNWRAP(&public_key, args[offset], Nothing<void>());
|
ASSIGN_OR_RETURN_UNWRAP(&public_key, args[offset], Nothing<void>());
|
||||||
ASSIGN_OR_RETURN_UNWRAP(&private_key, args[offset + 1], Nothing<void>());
|
ASSIGN_OR_RETURN_UNWRAP(&private_key, args[offset + 1], Nothing<void>());
|
||||||
|
|
||||||
if (private_key->Data().GetKeyType() != kKeyTypePrivate ||
|
CHECK(private_key->Data().GetKeyType() == kKeyTypePrivate);
|
||||||
public_key->Data().GetKeyType() != kKeyTypePublic) {
|
CHECK(public_key->Data().GetKeyType() != kKeyTypeSecret);
|
||||||
THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
|
|
||||||
return Nothing<void>();
|
|
||||||
}
|
|
||||||
|
|
||||||
params->public_key = public_key->Data().addRef();
|
params->public_key = public_key->Data().addRef();
|
||||||
params->private_key = private_key->Data().addRef();
|
params->private_key = private_key->Data().addRef();
|
||||||
|
|
@ -557,8 +516,20 @@ bool DHBitsTraits::DeriveBits(
|
||||||
Environment* env,
|
Environment* env,
|
||||||
const DHBitsConfig& params,
|
const DHBitsConfig& params,
|
||||||
ByteSource* out) {
|
ByteSource* out) {
|
||||||
*out = StatelessDiffieHellmanThreadsafe(params.private_key.GetAsymmetricKey(),
|
auto dp = DHPointer::stateless(params.private_key.GetAsymmetricKey(),
|
||||||
params.public_key.GetAsymmetricKey());
|
params.public_key.GetAsymmetricKey());
|
||||||
|
if (!dp) {
|
||||||
|
bool can_throw =
|
||||||
|
per_process::v8_initialized && Isolate::TryGetCurrent() != nullptr;
|
||||||
|
if (can_throw) {
|
||||||
|
unsigned long err = ERR_get_error(); // NOLINT(runtime/int)
|
||||||
|
if (err) ThrowCryptoError(env, err, "diffieHellman failed");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = ByteSource::Allocated(dp.release());
|
||||||
|
CHECK(!out->empty());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -611,7 +582,6 @@ void DiffieHellman::Initialize(Environment* env, Local<Object> target) {
|
||||||
make(FIXED_ONE_BYTE_STRING(env->isolate(), "DiffieHellmanGroup"),
|
make(FIXED_ONE_BYTE_STRING(env->isolate(), "DiffieHellmanGroup"),
|
||||||
DiffieHellmanGroup);
|
DiffieHellmanGroup);
|
||||||
|
|
||||||
SetMethodNoSideEffect(context, target, "statelessDH", Stateless);
|
|
||||||
DHKeyPairGenJob::Initialize(env, target);
|
DHKeyPairGenJob::Initialize(env, target);
|
||||||
DHKeyExportJob::Initialize(env, target);
|
DHKeyExportJob::Initialize(env, target);
|
||||||
DHBitsJob::Initialize(env, target);
|
DHBitsJob::Initialize(env, target);
|
||||||
|
|
@ -632,7 +602,6 @@ void DiffieHellman::RegisterExternalReferences(
|
||||||
registry->Register(SetPrivateKey);
|
registry->Register(SetPrivateKey);
|
||||||
|
|
||||||
registry->Register(Check);
|
registry->Register(Check);
|
||||||
registry->Register(Stateless);
|
|
||||||
|
|
||||||
DHKeyPairGenJob::RegisterExternalReferences(registry);
|
DHKeyPairGenJob::RegisterExternalReferences(registry);
|
||||||
DHKeyExportJob::RegisterExternalReferences(registry);
|
DHKeyExportJob::RegisterExternalReferences(registry);
|
||||||
|
|
|
||||||
220
test/parallel/test-crypto-dh-stateless-async.js
Normal file
220
test/parallel/test-crypto-dh-stateless-async.js
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
'use strict';
|
||||||
|
const common = require('../common');
|
||||||
|
if (!common.hasCrypto)
|
||||||
|
common.skip('missing crypto');
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const { hasOpenSSL3 } = require('../common/crypto');
|
||||||
|
|
||||||
|
assert.throws(() => crypto.diffieHellman(crypto.generateKeyPairSync('ec', { namedCurve: 'P-256' }), null), {
|
||||||
|
name: 'TypeError',
|
||||||
|
code: 'ERR_INVALID_ARG_TYPE',
|
||||||
|
message: 'The "callback" argument must be of type function. Received null'
|
||||||
|
});
|
||||||
|
|
||||||
|
function test({ publicKey: alicePublicKey, privateKey: alicePrivateKey },
|
||||||
|
{ publicKey: bobPublicKey, privateKey: bobPrivateKey },
|
||||||
|
expectedValue) {
|
||||||
|
crypto.diffieHellman({
|
||||||
|
privateKey: alicePrivateKey,
|
||||||
|
publicKey: bobPublicKey
|
||||||
|
}, common.mustSucceed((buf1) => {
|
||||||
|
if (expectedValue !== undefined)
|
||||||
|
assert.deepStrictEqual(buf1, expectedValue);
|
||||||
|
crypto.diffieHellman({
|
||||||
|
privateKey: bobPrivateKey,
|
||||||
|
publicKey: alicePublicKey
|
||||||
|
}, common.mustSucceed((buf2) => {
|
||||||
|
assert.deepStrictEqual(buf1, buf2);
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const alicePrivateKey = crypto.createPrivateKey({
|
||||||
|
key: '-----BEGIN PRIVATE KEY-----\n' +
|
||||||
|
'MIIBoQIBADCB1QYJKoZIhvcNAQMBMIHHAoHBAP//////////yQ/aoiFowjTExmKL\n' +
|
||||||
|
'gNwc0SkCTgiKZ8x0Agu+pjsTmyJRSgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVt\n' +
|
||||||
|
'bVHCReSFtXZiXn7G9ExC6aY37WsL/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR\n' +
|
||||||
|
'7ORbPcIAfLihY78FmNpINhxV05ppFj+o/STPX4NlXSPco62WHGLzViCFUrue1SkH\n' +
|
||||||
|
'cJaWbWcMNU5KvJgE8XRsCMojcyf//////////wIBAgSBwwKBwEh82IAVnYNf0Kjb\n' +
|
||||||
|
'qYSImDFyg9sH6CJ0GzRK05e6hM3dOSClFYi4kbA7Pr7zyfdn2SH6wSlNS14Jyrtt\n' +
|
||||||
|
'HePrRSeYl1T+tk0AfrvaLmyM56F+9B3jwt/nzqr5YxmfVdXb2aQV53VS/mm3pB2H\n' +
|
||||||
|
'iIt9FmvFaaOVe2DupqSr6xzbf/zyON+WF5B5HNVOWXswgpgdUsCyygs98hKy/Xje\n' +
|
||||||
|
'TGzJUoWInW39t0YgMXenJrkS0m6wol8Rhxx81AGgELNV7EHZqg==\n' +
|
||||||
|
'-----END PRIVATE KEY-----',
|
||||||
|
format: 'pem'
|
||||||
|
});
|
||||||
|
const alicePublicKey = crypto.createPublicKey({
|
||||||
|
key: '-----BEGIN PUBLIC KEY-----\n' +
|
||||||
|
'MIIBnzCB1QYJKoZIhvcNAQMBMIHHAoHBAP//////////yQ/aoiFowjTExmKLgNwc\n' +
|
||||||
|
'0SkCTgiKZ8x0Agu+pjsTmyJRSgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHC\n' +
|
||||||
|
'ReSFtXZiXn7G9ExC6aY37WsL/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7ORb\n' +
|
||||||
|
'PcIAfLihY78FmNpINhxV05ppFj+o/STPX4NlXSPco62WHGLzViCFUrue1SkHcJaW\n' +
|
||||||
|
'bWcMNU5KvJgE8XRsCMojcyf//////////wIBAgOBxAACgcBR7+iL5qx7aOb9K+aZ\n' +
|
||||||
|
'y2oLt7ST33sDKT+nxpag6cWDDWzPBKFDCJ8fr0v7yW453px8N4qi4R7SYYxFBaYN\n' +
|
||||||
|
'Y3JvgDg1ct2JC9sxSuUOLqSFn3hpmAjW7cS0kExIVGfdLlYtIqbhhuo45cTEbVIM\n' +
|
||||||
|
'rDEz8mjIlnvbWpKB9+uYmbjfVoc3leFvUBqfG2In2m23Md1swsPxr3n7g68H66JX\n' +
|
||||||
|
'iBJKZLQMqNdbY14G9rdKmhhTJrQjC+i7Q/wI8JPhOFzHIGA=\n' +
|
||||||
|
'-----END PUBLIC KEY-----',
|
||||||
|
format: 'pem'
|
||||||
|
});
|
||||||
|
|
||||||
|
const bobPrivateKey = crypto.createPrivateKey({
|
||||||
|
key: '-----BEGIN PRIVATE KEY-----\n' +
|
||||||
|
'MIIBoQIBADCB1QYJKoZIhvcNAQMBMIHHAoHBAP//////////yQ/aoiFowjTExmKL\n' +
|
||||||
|
'gNwc0SkCTgiKZ8x0Agu+pjsTmyJRSgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVt\n' +
|
||||||
|
'bVHCReSFtXZiXn7G9ExC6aY37WsL/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR\n' +
|
||||||
|
'7ORbPcIAfLihY78FmNpINhxV05ppFj+o/STPX4NlXSPco62WHGLzViCFUrue1SkH\n' +
|
||||||
|
'cJaWbWcMNU5KvJgE8XRsCMojcyf//////////wIBAgSBwwKBwHxnT7Zw2Ehh1vyw\n' +
|
||||||
|
'eolzQFHQzyuT0y+3BF+FxK2Ox7VPguTp57wQfGHbORJ2cwCdLx2mFM7gk4tZ6COS\n' +
|
||||||
|
'E3Vta85a/PuhKXNLRdP79JgLnNtVtKXB+ePDS5C2GgXH1RHvqEdJh7JYnMy7Zj4P\n' +
|
||||||
|
'GagGtIy3dV5f4FA0B/2C97jQ1pO16ah8gSLQRKsNpTCw2rqsZusE0rK6RaYAef7H\n' +
|
||||||
|
'y/0tmLIsHxLIn+WK9CANqMbCWoP4I178BQaqhiOBkNyNZ0ndqA==\n' +
|
||||||
|
'-----END PRIVATE KEY-----',
|
||||||
|
format: 'pem'
|
||||||
|
});
|
||||||
|
|
||||||
|
const bobPublicKey = crypto.createPublicKey({
|
||||||
|
key: '-----BEGIN PUBLIC KEY-----\n' +
|
||||||
|
'MIIBoDCB1QYJKoZIhvcNAQMBMIHHAoHBAP//////////yQ/aoiFowjTExmKLgNwc\n' +
|
||||||
|
'0SkCTgiKZ8x0Agu+pjsTmyJRSgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHC\n' +
|
||||||
|
'ReSFtXZiXn7G9ExC6aY37WsL/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7ORb\n' +
|
||||||
|
'PcIAfLihY78FmNpINhxV05ppFj+o/STPX4NlXSPco62WHGLzViCFUrue1SkHcJaW\n' +
|
||||||
|
'bWcMNU5KvJgE8XRsCMojcyf//////////wIBAgOBxQACgcEAi26oq8z/GNSBm3zi\n' +
|
||||||
|
'gNt7SA7cArUBbTxINa9iLYWp6bxrvCKwDQwISN36/QUw8nUAe8aRyMt0oYn+y6vW\n' +
|
||||||
|
'Pw5OlO+TLrUelMVFaADEzoYomH0zVGb0sW4aBN8haC0mbrPt9QshgCvjr1hEPEna\n' +
|
||||||
|
'QFKfjzNaJRNMFFd4f2Dn8MSB4yu1xpA1T2i0JSk24vS2H55jx24xhUYtfhT2LJgK\n' +
|
||||||
|
'JvnaODey/xtY4Kql10ZKf43Lw6gdQC3G8opC9OxVxt9oNR7Z\n' +
|
||||||
|
'-----END PUBLIC KEY-----',
|
||||||
|
format: 'pem'
|
||||||
|
});
|
||||||
|
|
||||||
|
const privateKey = Buffer.from(
|
||||||
|
'487CD880159D835FD0A8DBA9848898317283DB07E822741B344AD397BA84CDDD3920A51588' +
|
||||||
|
'B891B03B3EBEF3C9F767D921FAC1294D4B5E09CABB6D1DE3EB4527989754FEB64D007EBBDA' +
|
||||||
|
'2E6C8CE7A17EF41DE3C2DFE7CEAAF963199F55D5DBD9A415E77552FE69B7A41D87888B7D16' +
|
||||||
|
'6BC569A3957B60EEA6A4ABEB1CDB7FFCF238DF961790791CD54E597B3082981D52C0B2CA0B' +
|
||||||
|
'3DF212B2FD78DE4C6CC95285889D6DFDB746203177A726B912D26EB0A25F11871C7CD401A0' +
|
||||||
|
'10B355EC41D9AA', 'hex');
|
||||||
|
const publicKey = Buffer.from(
|
||||||
|
'8b6ea8abccff18d4819b7ce280db7b480edc02b5016d3c4835af622d85a9e9bc6bbc22b00d' +
|
||||||
|
'0c0848ddfafd0530f275007bc691c8cb74a189fecbabd63f0e4e94ef932eb51e94c5456800' +
|
||||||
|
'c4ce8628987d335466f4b16e1a04df21682d266eb3edf50b21802be3af58443c49da40529f' +
|
||||||
|
'8f335a25134c1457787f60e7f0c481e32bb5c690354f68b4252936e2f4b61f9e63c76e3185' +
|
||||||
|
'462d7e14f62c980a26f9da3837b2ff1b58e0aaa5d7464a7f8dcbc3a81d402dc6f28a42f4ec' +
|
||||||
|
'55c6df68351ed9', 'hex');
|
||||||
|
|
||||||
|
const group = crypto.getDiffieHellman('modp5');
|
||||||
|
const dh = crypto.createDiffieHellman(group.getPrime(), group.getGenerator());
|
||||||
|
dh.setPrivateKey(privateKey);
|
||||||
|
|
||||||
|
// Test simple Diffie-Hellman, no curves involved.
|
||||||
|
test({ publicKey: alicePublicKey, privateKey: alicePrivateKey },
|
||||||
|
{ publicKey: bobPublicKey, privateKey: bobPrivateKey },
|
||||||
|
dh.computeSecret(publicKey));
|
||||||
|
|
||||||
|
test(crypto.generateKeyPairSync('dh', { group: 'modp5' }),
|
||||||
|
crypto.generateKeyPairSync('dh', { group: 'modp5' }));
|
||||||
|
|
||||||
|
test(crypto.generateKeyPairSync('dh', { group: 'modp5' }),
|
||||||
|
crypto.generateKeyPairSync('dh', { prime: group.getPrime() }));
|
||||||
|
|
||||||
|
const list = [
|
||||||
|
// Same generator, but different primes.
|
||||||
|
[{ group: 'modp5' }, { group: 'modp18' }]];
|
||||||
|
|
||||||
|
// TODO(danbev): Take a closer look if there should be a check in OpenSSL3
|
||||||
|
// when the dh parameters differ.
|
||||||
|
if (!hasOpenSSL3) {
|
||||||
|
// Same primes, but different generator.
|
||||||
|
list.push([{ group: 'modp5' }, { prime: group.getPrime(), generator: 5 }]);
|
||||||
|
// Same generator, but different primes.
|
||||||
|
list.push([{ primeLength: 1024 }, { primeLength: 1024 }]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [params1, params2] of list) {
|
||||||
|
crypto.diffieHellman({
|
||||||
|
privateKey: crypto.generateKeyPairSync('dh', params1).privateKey,
|
||||||
|
publicKey: crypto.generateKeyPairSync('dh', params2).publicKey
|
||||||
|
}, common.mustCall((err) => {
|
||||||
|
assert.ok(err);
|
||||||
|
assert.strictEqual(err.name, 'Error');
|
||||||
|
assert.match(err.message, hasOpenSSL3 ? /mismatching domain parameters/ : /different parameters/);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const privateKey = crypto.createPrivateKey({
|
||||||
|
key: '-----BEGIN PRIVATE KEY-----\n' +
|
||||||
|
'MIIBoQIBADCB1QYJKoZIhvcNAQMBMIHHAoHBAP//////////yQ/aoiFowjTExmKL\n' +
|
||||||
|
'gNwc0SkCTgiKZ8x0Agu+pjsTmyJRSgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVt\n' +
|
||||||
|
'bVHCReSFtXZiXn7G9ExC6aY37WsL/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR\n' +
|
||||||
|
'7ORbPcIAfLihY78FmNpINhxV05ppFj+o/STPX4NlXSPco62WHGLzViCFUrue1SkH\n' +
|
||||||
|
'cJaWbWcMNU5KvJgE8XRsCMojcyf//////////wIBAgSBwwKBwHu9fpiqrfJJ+tl9\n' +
|
||||||
|
'ujFtEWv4afub6A/1/7sgishOYN3YQ+nmWQlmPpveIY34an5dG82CTrixHwUzQTMF\n' +
|
||||||
|
'JaiCW3ax9+qk31f2jTNKrQznmKgopVKXF0FEJC6H79W/8Y0U14gsI9sHpovKhfou\n' +
|
||||||
|
'RQD0QogW7ejSwMG8hCYibfrvMm0b5PHlwimISyEKh7VtDQ1frYN/Wr9ZbiV+FePJ\n' +
|
||||||
|
'2j6RUKYNj1Pv+B4zdMgiLLjILAs8WUfbHciU21KSJh1izVQaUQ==\n' +
|
||||||
|
'-----END PRIVATE KEY-----'
|
||||||
|
});
|
||||||
|
const publicKey = crypto.createPublicKey({
|
||||||
|
key: '-----BEGIN PUBLIC KEY-----\n' +
|
||||||
|
'MIIBoDCB1QYJKoZIhvcNAQMBMIHHAoHBAP//////////yQ/aoiFowjTExmKLgNwc\n' +
|
||||||
|
'0SkCTgiKZ8x0Agu+pjsTmyJRSgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHC\n' +
|
||||||
|
'ReSFtXZiXn7G9ExC6aY37WsL/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7ORb\n' +
|
||||||
|
'PcIAfLihY78FmNpINhxV05ppFj+o/STPX4NlXSPco62WHGLzViCFUrue1SkHcJaW\n' +
|
||||||
|
'bWcMNU5KvJgE8XRsCMojcyf//////////wIBAgOBxQACgcEAmG9LpD8SAA6/W7oK\n' +
|
||||||
|
'E4MCuuQtf5E8bqtcEAfYTOOvKyCS+eiX3TtZRsvHJjUBEyeO99PR/KrGVlkSuW52\n' +
|
||||||
|
'ZOSXUOFu1L/0tqHrvRVHo+QEq3OvZ3EAyJkdtSEUTztxuUrMOyJXHDc1OUdNSnk0\n' +
|
||||||
|
'taGX4mP3247golVx2DS4viDYs7UtaMdx03dWaP6y5StNUZQlgCIUzL7MYpC16V5y\n' +
|
||||||
|
'KkFrE+Kp/Z77gEjivaG6YuxVj4GPLxJYbNFVTel42oSVeKuq\n' +
|
||||||
|
'-----END PUBLIC KEY-----',
|
||||||
|
format: 'pem'
|
||||||
|
});
|
||||||
|
|
||||||
|
// This key combination will result in an unusually short secret, and should
|
||||||
|
// not cause an assertion failure.
|
||||||
|
crypto.diffieHellman({ publicKey, privateKey }, common.mustSucceed((secret) => {
|
||||||
|
assert.strictEqual(secret.toString('hex'),
|
||||||
|
'0099d0fa242af5db9ea7330e23937a27db041f79c581500fc7f9976' +
|
||||||
|
'554d59d5b9ced934778d72e19a1fefc81e9d981013198748c0b5c6c' +
|
||||||
|
'762985eec687dc5bec5c9367b05837daee9d0bcc29024ed7f3abba1' +
|
||||||
|
'2794b65a745117fb0d87bc5b1b2b68c296c3f686cc29e450e4e1239' +
|
||||||
|
'21f56a5733fe58aabf71f14582954059c2185d342b9b0fa10c2598a' +
|
||||||
|
'5426c2baee7f9a686fc1e16cd4757c852bf7225a2732250548efe28' +
|
||||||
|
'debc26f1acdec51efe23d20786a6f8a14d360803bbc71972e87fd3');
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test ECDH.
|
||||||
|
|
||||||
|
test(crypto.generateKeyPairSync('ec', { namedCurve: 'P-256' }),
|
||||||
|
crypto.generateKeyPairSync('ec', { namedCurve: 'P-256' }));
|
||||||
|
|
||||||
|
crypto.diffieHellman({
|
||||||
|
privateKey: crypto.generateKeyPairSync('ec', { namedCurve: 'P-256' }).privateKey,
|
||||||
|
publicKey: crypto.generateKeyPairSync('ec', { namedCurve: 'P-384' }).publicKey
|
||||||
|
}, common.mustCall((err) => {
|
||||||
|
assert.ok(err);
|
||||||
|
assert.strictEqual(err.name, 'Error');
|
||||||
|
assert.match(err.message, hasOpenSSL3 ? /mismatching domain parameters/ : /different parameters/);
|
||||||
|
}));
|
||||||
|
|
||||||
|
test(crypto.generateKeyPairSync('x448'),
|
||||||
|
crypto.generateKeyPairSync('x448'));
|
||||||
|
|
||||||
|
test(crypto.generateKeyPairSync('x25519'),
|
||||||
|
crypto.generateKeyPairSync('x25519'));
|
||||||
|
|
||||||
|
{
|
||||||
|
const { privateKey } = crypto.generateKeyPairSync('x25519');
|
||||||
|
const publicKey = crypto.createPublicKey('-----BEGIN PUBLIC KEY-----\n' +
|
||||||
|
'MCowBQYDK2VuAyEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n' +
|
||||||
|
'-----END PUBLIC KEY-----');
|
||||||
|
crypto.diffieHellman({ publicKey, privateKey }, common.mustCall((err) => {
|
||||||
|
assert.ok(err);
|
||||||
|
assert.strictEqual(err.name, 'Error');
|
||||||
|
assert.match(err.message, hasOpenSSL3 ? /failed during derivation/ : /Deriving bits failed/);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
@ -38,7 +38,12 @@ function test({ publicKey: alicePublicKey, privateKey: alicePrivateKey },
|
||||||
privateKey: bobPrivateKey,
|
privateKey: bobPrivateKey,
|
||||||
publicKey: alicePublicKey
|
publicKey: alicePublicKey
|
||||||
});
|
});
|
||||||
|
const buf3 = crypto.diffieHellman({
|
||||||
|
privateKey: bobPrivateKey,
|
||||||
|
publicKey: alicePrivateKey
|
||||||
|
});
|
||||||
assert.deepStrictEqual(buf1, buf2);
|
assert.deepStrictEqual(buf1, buf2);
|
||||||
|
assert.deepStrictEqual(buf1, buf3);
|
||||||
|
|
||||||
if (expectedValue !== undefined)
|
if (expectedValue !== undefined)
|
||||||
assert.deepStrictEqual(buf1, expectedValue);
|
assert.deepStrictEqual(buf1, expectedValue);
|
||||||
|
|
@ -214,13 +219,12 @@ for (const [params1, params2] of list) {
|
||||||
|
|
||||||
// Test ECDH.
|
// Test ECDH.
|
||||||
|
|
||||||
test(crypto.generateKeyPairSync('ec', { namedCurve: 'secp256k1' }),
|
test(crypto.generateKeyPairSync('ec', { namedCurve: 'P-256' }),
|
||||||
crypto.generateKeyPairSync('ec', { namedCurve: 'secp256k1' }));
|
crypto.generateKeyPairSync('ec', { namedCurve: 'P-256' }));
|
||||||
|
|
||||||
const not256k1 = crypto.getCurves().find((c) => /^sec.*(224|384|512)/.test(c));
|
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
test(crypto.generateKeyPairSync('ec', { namedCurve: 'secp256k1' }),
|
test(crypto.generateKeyPairSync('ec', { namedCurve: 'P-256' }),
|
||||||
crypto.generateKeyPairSync('ec', { namedCurve: not256k1 }));
|
crypto.generateKeyPairSync('ec', { namedCurve: 'P-384' }));
|
||||||
}, hasOpenSSL3 ? {
|
}, hasOpenSSL3 ? {
|
||||||
name: 'Error',
|
name: 'Error',
|
||||||
code: 'ERR_OSSL_MISMATCHING_DOMAIN_PARAMETERS'
|
code: 'ERR_OSSL_MISMATCHING_DOMAIN_PARAMETERS'
|
||||||
|
|
@ -229,8 +233,6 @@ assert.throws(() => {
|
||||||
code: 'ERR_OSSL_EVP_DIFFERENT_PARAMETERS'
|
code: 'ERR_OSSL_EVP_DIFFERENT_PARAMETERS'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test ECDH-ES.
|
|
||||||
|
|
||||||
test(crypto.generateKeyPairSync('x448'),
|
test(crypto.generateKeyPairSync('x448'),
|
||||||
crypto.generateKeyPairSync('x448'));
|
crypto.generateKeyPairSync('x448'));
|
||||||
|
|
||||||
|
|
@ -245,3 +247,58 @@ assert.throws(() => {
|
||||||
code: 'ERR_CRYPTO_INCOMPATIBLE_KEY',
|
code: 'ERR_CRYPTO_INCOMPATIBLE_KEY',
|
||||||
message: 'Incompatible key types for Diffie-Hellman: x448 and x25519'
|
message: 'Incompatible key types for Diffie-Hellman: x448 and x25519'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
{
|
||||||
|
const kp = {
|
||||||
|
privateKey: crypto.generateKeySync('aes', { length: 128 }),
|
||||||
|
publicKey: crypto.generateKeyPairSync('x25519').publicKey,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
test(kp, kp);
|
||||||
|
}, {
|
||||||
|
code: 'ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE',
|
||||||
|
message: 'Invalid key object type secret, expected private.'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const kp = {
|
||||||
|
privateKey: crypto.generateKeyPairSync('x25519').publicKey,
|
||||||
|
publicKey: crypto.generateKeyPairSync('x25519').privateKey,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
test(kp, kp);
|
||||||
|
}, {
|
||||||
|
code: 'ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE',
|
||||||
|
message: 'Invalid key object type public, expected private.'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const kp = {
|
||||||
|
privateKey: crypto.generateKeyPairSync('x25519').privateKey,
|
||||||
|
publicKey: crypto.generateKeySync('aes', { length: 128 }),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
test(kp, kp);
|
||||||
|
}, {
|
||||||
|
code: 'ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE',
|
||||||
|
message: 'Invalid key object type secret, expected private or public.'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const { privateKey } = crypto.generateKeyPairSync('x25519');
|
||||||
|
const publicKey = crypto.createPublicKey('-----BEGIN PUBLIC KEY-----\n' +
|
||||||
|
'MCowBQYDK2VuAyEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n' +
|
||||||
|
'-----END PUBLIC KEY-----');
|
||||||
|
assert.throws(
|
||||||
|
() => crypto.diffieHellman({ publicKey, privateKey }),
|
||||||
|
hasOpenSSL3 ?
|
||||||
|
{ name: 'Error', code: 'ERR_OSSL_FAILED_DURING_DERIVATION' } :
|
||||||
|
{ name: 'Error', message: /Deriving bits failed/ },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user