Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/api/crypto.md14
-rw-r--r--lib/internal/crypto/keys.js10
-rw-r--r--src/crypto/crypto_keys.cc50
-rw-r--r--src/crypto/crypto_keys.h1
-rw-r--r--test/parallel/test-crypto-key-objects.js49
5 files changed, 124 insertions, 0 deletions
diff --git a/doc/api/crypto.md b/doc/api/crypto.md
index 470c8f66418..8025e0f9182 100644
--- a/doc/api/crypto.md
+++ b/doc/api/crypto.md
@@ -2082,6 +2082,20 @@ encryption mechanism, PEM-level encryption is not supported when encrypting
a PKCS#8 key. See [RFC 5208][] for PKCS#8 encryption and [RFC 1421][] for
PKCS#1 and SEC1 encryption.
+### `keyObject.equals(otherKeyObject)`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* `otherKeyObject`: {KeyObject} A `KeyObject` with which to
+ compare `keyObject`.
+* Returns: {boolean}
+
+Returns `true` or `false` depending on whether the keys have exactly the same
+type, value, and parameters. This method is not
+[constant time](https://en.wikipedia.org/wiki/Timing_attack).
+
### `keyObject.symmetricKeySize`
<!-- YAML
diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js
index ea2ecc262ed..5c9f1fd0926 100644
--- a/lib/internal/crypto/keys.js
+++ b/lib/internal/crypto/keys.js
@@ -124,6 +124,16 @@ const {
throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key);
return key[kKeyObject];
}
+
+ equals(otherKeyObject) {
+ if (!isKeyObject(otherKeyObject)) {
+ throw new ERR_INVALID_ARG_TYPE(
+ 'otherKeyObject', 'KeyObject', otherKeyObject);
+ }
+
+ return otherKeyObject.type === this.type &&
+ this[kHandle].equals(otherKeyObject[kHandle]);
+ }
}
class SecretKeyObject extends KeyObject {
diff --git a/src/crypto/crypto_keys.cc b/src/crypto/crypto_keys.cc
index c5b3e6b3718..08b95190698 100644
--- a/src/crypto/crypto_keys.cc
+++ b/src/crypto/crypto_keys.cc
@@ -921,6 +921,7 @@ v8::Local<v8::Function> KeyObjectHandle::Initialize(Environment* env) {
env->SetProtoMethod(t, "initEDRaw", InitEDRaw);
env->SetProtoMethod(t, "initJwk", InitJWK);
env->SetProtoMethod(t, "keyDetail", GetKeyDetail);
+ env->SetProtoMethod(t, "equals", Equals);
auto function = t->GetFunction(env->context()).ToLocalChecked();
env->set_crypto_key_object_handle_constructor(function);
@@ -939,6 +940,7 @@ void KeyObjectHandle::RegisterExternalReferences(
registry->Register(InitEDRaw);
registry->Register(InitJWK);
registry->Register(GetKeyDetail);
+ registry->Register(Equals);
}
MaybeLocal<Object> KeyObjectHandle::Create(
@@ -1134,6 +1136,54 @@ void KeyObjectHandle::InitEDRaw(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(true);
}
+void KeyObjectHandle::Equals(const FunctionCallbackInfo<Value>& args) {
+ KeyObjectHandle* self_handle;
+ KeyObjectHandle* arg_handle;
+ ASSIGN_OR_RETURN_UNWRAP(&self_handle, args.Holder());
+ ASSIGN_OR_RETURN_UNWRAP(&arg_handle, args[0].As<Object>());
+ std::shared_ptr<KeyObjectData> key = self_handle->Data();
+ std::shared_ptr<KeyObjectData> key2 = arg_handle->Data();
+
+ KeyType key_type = key->GetKeyType();
+ CHECK_EQ(key_type, key2->GetKeyType());
+
+ bool ret;
+ switch (key_type) {
+ case kKeyTypeSecret: {
+ size_t size = key->GetSymmetricKeySize();
+ if (size == key2->GetSymmetricKeySize()) {
+ ret = CRYPTO_memcmp(
+ key->GetSymmetricKey(),
+ key2->GetSymmetricKey(),
+ size) == 0;
+ } else {
+ ret = false;
+ }
+ break;
+ }
+ case kKeyTypePublic:
+ case kKeyTypePrivate: {
+ EVP_PKEY* pkey = key->GetAsymmetricKey().get();
+ EVP_PKEY* pkey2 = key2->GetAsymmetricKey().get();
+#if OPENSSL_VERSION_MAJOR >= 3
+ int ok = EVP_PKEY_eq(pkey, pkey2);
+#else
+ int ok = EVP_PKEY_cmp(pkey, pkey2);
+#endif
+ if (ok == -2) {
+ Environment* env = Environment::GetCurrent(args);
+ return THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env);
+ }
+ ret = ok == 1;
+ break;
+ }
+ default:
+ UNREACHABLE("unsupported key type");
+ }
+
+ args.GetReturnValue().Set(ret);
+}
+
void KeyObjectHandle::GetKeyDetail(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
KeyObjectHandle* key;
diff --git a/src/crypto/crypto_keys.h b/src/crypto/crypto_keys.h
index 48d38cd20b0..d06565d8ad7 100644
--- a/src/crypto/crypto_keys.h
+++ b/src/crypto/crypto_keys.h
@@ -189,6 +189,7 @@ class KeyObjectHandle : public BaseObject {
static void InitEDRaw(const v8::FunctionCallbackInfo<v8::Value>& args);
static void InitJWK(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetKeyDetail(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Equals(const v8::FunctionCallbackInfo<v8::Value>& args);
static void ExportJWK(const v8::FunctionCallbackInfo<v8::Value>& args);
diff --git a/test/parallel/test-crypto-key-objects.js b/test/parallel/test-crypto-key-objects.js
index c564c4dcb43..e543ff730dc 100644
--- a/test/parallel/test-crypto-key-objects.js
+++ b/test/parallel/test-crypto-key-objects.js
@@ -21,6 +21,7 @@ const {
privateDecrypt,
privateEncrypt,
getCurves,
+ generateKeySync,
generateKeyPairSync,
webcrypto,
} = require('crypto');
@@ -846,3 +847,51 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
assert(!isKeyObject(cryptoKey));
});
}
+
+{
+ const first = Buffer.from('Hello');
+ const second = Buffer.from('World');
+ const keyObject = createSecretKey(first);
+ assert(createSecretKey(first).equals(createSecretKey(first)));
+ assert(!createSecretKey(first).equals(createSecretKey(second)));
+
+ assert.throws(() => keyObject.equals(0), {
+ name: 'TypeError',
+ code: 'ERR_INVALID_ARG_TYPE',
+ message: 'The "otherKeyObject" argument must be an instance of KeyObject. Received type number (0)'
+ });
+
+ assert(keyObject.equals(keyObject));
+ assert(!keyObject.equals(createPublicKey(publicPem)));
+ assert(!keyObject.equals(createPrivateKey(privatePem)));
+}
+
+{
+ const first = generateKeyPairSync('ed25519');
+ const second = generateKeyPairSync('ed25519');
+ const secret = generateKeySync('aes', { length: 128 });
+
+ assert(first.publicKey.equals(first.publicKey));
+ assert(first.publicKey.equals(createPublicKey(
+ first.publicKey.export({ format: 'pem', type: 'spki' }))));
+ assert(!first.publicKey.equals(second.publicKey));
+ assert(!first.publicKey.equals(second.privateKey));
+ assert(!first.publicKey.equals(secret));
+
+ assert(first.privateKey.equals(first.privateKey));
+ assert(first.privateKey.equals(createPrivateKey(
+ first.privateKey.export({ format: 'pem', type: 'pkcs8' }))));
+ assert(!first.privateKey.equals(second.privateKey));
+ assert(!first.privateKey.equals(second.publicKey));
+ assert(!first.privateKey.equals(secret));
+}
+
+{
+ const first = generateKeyPairSync('ed25519');
+ const second = generateKeyPairSync('ed448');
+
+ assert(!first.publicKey.equals(second.publicKey));
+ assert(!first.publicKey.equals(second.privateKey));
+ assert(!first.privateKey.equals(second.privateKey));
+ assert(!first.privateKey.equals(second.publicKey));
+}