crypto: add KeyObject.prototype.equals method
PR-URL: https://github.com/nodejs/node/pull/42093 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
This commit is contained in:
parent
bcc523e389
commit
aa97c9d973
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user