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
|
a PKCS#8 key. See [RFC 5208][] for PKCS#8 encryption and [RFC 1421][] for
|
||||||
PKCS#1 and SEC1 encryption.
|
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`
|
### `keyObject.symmetricKeySize`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
|
@ -124,6 +124,16 @@ const {
|
|||||||
throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key);
|
throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key);
|
||||||
return key[kKeyObject];
|
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 {
|
class SecretKeyObject extends KeyObject {
|
||||||
|
@ -921,6 +921,7 @@ v8::Local<v8::Function> KeyObjectHandle::Initialize(Environment* env) {
|
|||||||
env->SetProtoMethod(t, "initEDRaw", InitEDRaw);
|
env->SetProtoMethod(t, "initEDRaw", InitEDRaw);
|
||||||
env->SetProtoMethod(t, "initJwk", InitJWK);
|
env->SetProtoMethod(t, "initJwk", InitJWK);
|
||||||
env->SetProtoMethod(t, "keyDetail", GetKeyDetail);
|
env->SetProtoMethod(t, "keyDetail", GetKeyDetail);
|
||||||
|
env->SetProtoMethod(t, "equals", Equals);
|
||||||
|
|
||||||
auto function = t->GetFunction(env->context()).ToLocalChecked();
|
auto function = t->GetFunction(env->context()).ToLocalChecked();
|
||||||
env->set_crypto_key_object_handle_constructor(function);
|
env->set_crypto_key_object_handle_constructor(function);
|
||||||
@ -939,6 +940,7 @@ void KeyObjectHandle::RegisterExternalReferences(
|
|||||||
registry->Register(InitEDRaw);
|
registry->Register(InitEDRaw);
|
||||||
registry->Register(InitJWK);
|
registry->Register(InitJWK);
|
||||||
registry->Register(GetKeyDetail);
|
registry->Register(GetKeyDetail);
|
||||||
|
registry->Register(Equals);
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeLocal<Object> KeyObjectHandle::Create(
|
MaybeLocal<Object> KeyObjectHandle::Create(
|
||||||
@ -1134,6 +1136,54 @@ void KeyObjectHandle::InitEDRaw(const FunctionCallbackInfo<Value>& args) {
|
|||||||
args.GetReturnValue().Set(true);
|
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) {
|
void KeyObjectHandle::GetKeyDetail(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
KeyObjectHandle* key;
|
KeyObjectHandle* key;
|
||||||
|
@ -189,6 +189,7 @@ class KeyObjectHandle : public BaseObject {
|
|||||||
static void InitEDRaw(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void InitEDRaw(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void InitJWK(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 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);
|
static void ExportJWK(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ const {
|
|||||||
privateDecrypt,
|
privateDecrypt,
|
||||||
privateEncrypt,
|
privateEncrypt,
|
||||||
getCurves,
|
getCurves,
|
||||||
|
generateKeySync,
|
||||||
generateKeyPairSync,
|
generateKeyPairSync,
|
||||||
webcrypto,
|
webcrypto,
|
||||||
} = require('crypto');
|
} = require('crypto');
|
||||||
@ -846,3 +847,51 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
|
|||||||
assert(!isKeyObject(cryptoKey));
|
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