Skip to content

Commit

Permalink
src: store key data in separate class
Browse files Browse the repository at this point in the history
This separates key handles from the actual key data:

+-----------------+
| NativeKeyObject |
+-----------------+
        ^
     extends
        |
+-----------------+    +-----------------+    +---------------+
| KeyObject  (JS) | -> | KeyObjectHandle | -> | KeyObjectData |
+-----------------+    +-----------------+    +---------------+

PR-URL: #33360
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
tniessen authored and addaleax committed Sep 27, 2020
1 parent 4b98356 commit 48a8339
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 105 deletions.
12 changes: 6 additions & 6 deletions lib/internal/crypto/keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -331,23 +331,23 @@ function createSecretKey(key) {
key = prepareSecretKey(key, true);
if (key.byteLength === 0)
throw new ERR_OUT_OF_RANGE('key.byteLength', '> 0', key.byteLength);
const handle = new KeyObjectHandle(kKeyTypeSecret);
handle.init(key);
const handle = new KeyObjectHandle();
handle.init(kKeyTypeSecret, key);
return new SecretKeyObject(handle);
}

function createPublicKey(key) {
const { format, type, data } = prepareAsymmetricKey(key, kCreatePublic);
const handle = new KeyObjectHandle(kKeyTypePublic);
handle.init(data, format, type);
const handle = new KeyObjectHandle();
handle.init(kKeyTypePublic, data, format, type);
return new PublicKeyObject(handle);
}

function createPrivateKey(key) {
const { format, type, data, passphrase } =
prepareAsymmetricKey(key, kCreatePrivate);
const handle = new KeyObjectHandle(kKeyTypePrivate);
handle.init(data, format, type, passphrase);
const handle = new KeyObjectHandle();
handle.init(kKeyTypePrivate, data, format, type, passphrase);
return new PrivateKeyObject(handle);
}

Expand Down
169 changes: 86 additions & 83 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2890,7 +2890,8 @@ ByteSource ByteSource::FromSymmetricKeyObjectHandle(Local<Value> handle) {
CHECK(handle->IsObject());
KeyObjectHandle* key = Unwrap<KeyObjectHandle>(handle.As<Object>());
CHECK_NOT_NULL(key);
return Foreign(key->GetSymmetricKey(), key->GetSymmetricKeySize());
return Foreign(key->Data()->GetSymmetricKey(),
key->Data()->GetSymmetricKeySize());
}

ByteSource::ByteSource(const char* data, char* allocated_data, size_t size)
Expand Down Expand Up @@ -3036,9 +3037,9 @@ static ManagedEVPPKey GetPrivateKeyFromJs(
CHECK(args[*offset]->IsObject() && allow_key_object);
KeyObjectHandle* key;
ASSIGN_OR_RETURN_UNWRAP(&key, args[*offset].As<Object>(), ManagedEVPPKey());
CHECK_EQ(key->GetKeyType(), kKeyTypePrivate);
CHECK_EQ(key->Data()->GetKeyType(), kKeyTypePrivate);
(*offset) += 4;
return key->GetAsymmetricKey();
return key->Data()->GetAsymmetricKey();
}
}

Expand Down Expand Up @@ -3096,9 +3097,9 @@ static ManagedEVPPKey GetPublicOrPrivateKeyFromJs(
CHECK(args[*offset]->IsObject());
KeyObjectHandle* key = Unwrap<KeyObjectHandle>(args[*offset].As<Object>());
CHECK_NOT_NULL(key);
CHECK_NE(key->GetKeyType(), kKeyTypeSecret);
CHECK_NE(key->Data()->GetKeyType(), kKeyTypeSecret);
(*offset) += 4;
return key->GetAsymmetricKey();
return key->Data()->GetAsymmetricKey();
}
}

Expand Down Expand Up @@ -3205,6 +3206,48 @@ EVP_PKEY* ManagedEVPPKey::get() const {
return pkey_.get();
}

KeyObjectData* KeyObjectData::CreateSecret(v8::Local<v8::ArrayBufferView> abv) {
size_t key_len = abv->ByteLength();
char* mem = MallocOpenSSL<char>(key_len);
abv->CopyContents(mem, key_len);
KeyObjectData* data = new KeyObjectData();
data->key_type_ = kKeyTypeSecret;
data->symmetric_key_ = std::unique_ptr<char, std::function<void(char*)>>(mem,
[key_len](char* p) {
OPENSSL_clear_free(p, key_len);
});
data->symmetric_key_len_ = key_len;
return data;
}

KeyObjectData* KeyObjectData::CreateAsymmetric(KeyType key_type,
const ManagedEVPPKey& pkey) {
CHECK(pkey);
KeyObjectData* data = new KeyObjectData();
data->key_type_ = key_type;
data->asymmetric_key_ = pkey;
return data;
}

KeyType KeyObjectData::GetKeyType() const {
return key_type_;
}

ManagedEVPPKey KeyObjectData::GetAsymmetricKey() const {
CHECK_NE(key_type_, kKeyTypeSecret);
return asymmetric_key_;
}

const char* KeyObjectData::GetSymmetricKey() const {
CHECK_EQ(key_type_, kKeyTypeSecret);
return symmetric_key_.get();
}

size_t KeyObjectData::GetSymmetricKeySize() const {
CHECK_EQ(key_type_, kKeyTypeSecret);
return symmetric_key_len_;
}

Local<Function> KeyObjectHandle::Initialize(Environment* env,
Local<Object> target) {
Local<FunctionTemplate> t = env->NewFunctionTemplate(New);
Expand Down Expand Up @@ -3241,46 +3284,23 @@ MaybeLocal<Object> KeyObjectHandle::Create(Environment* env,

KeyObjectHandle* key = Unwrap<KeyObjectHandle>(obj);
CHECK_NOT_NULL(key);
if (key_type == kKeyTypePublic)
key->InitPublic(pkey);
else
key->InitPrivate(pkey);
key->data_.reset(KeyObjectData::CreateAsymmetric(key_type, pkey));
return obj;
}

ManagedEVPPKey KeyObjectHandle::GetAsymmetricKey() const {
CHECK_NE(key_type_, kKeyTypeSecret);
return this->asymmetric_key_;
}

const char* KeyObjectHandle::GetSymmetricKey() const {
CHECK_EQ(key_type_, kKeyTypeSecret);
return this->symmetric_key_.get();
}

size_t KeyObjectHandle::GetSymmetricKeySize() const {
CHECK_EQ(key_type_, kKeyTypeSecret);
return this->symmetric_key_len_;
const KeyObjectData* KeyObjectHandle::Data() {
return data_.get();
}

void KeyObjectHandle::New(const FunctionCallbackInfo<Value>& args) {
CHECK(args.IsConstructCall());
CHECK(args[0]->IsInt32());
KeyType key_type = static_cast<KeyType>(args[0].As<Uint32>()->Value());
Environment* env = Environment::GetCurrent(args);
new KeyObjectHandle(env, args.This(), key_type);
}

KeyType KeyObjectHandle::GetKeyType() const {
return this->key_type_;
new KeyObjectHandle(env, args.This());
}

KeyObjectHandle::KeyObjectHandle(Environment* env,
Local<Object> wrap,
KeyType key_type)
: BaseObject(env, wrap),
key_type_(key_type),
symmetric_key_(nullptr, nullptr) {
Local<Object> wrap)
: BaseObject(env, wrap) {
MakeWeak();
}

Expand All @@ -3289,66 +3309,45 @@ void KeyObjectHandle::Init(const FunctionCallbackInfo<Value>& args) {
ASSIGN_OR_RETURN_UNWRAP(&key, args.Holder());
MarkPopErrorOnReturn mark_pop_error_on_return;

CHECK(args[0]->IsInt32());
KeyType type = static_cast<KeyType>(args[0].As<Uint32>()->Value());

unsigned int offset;
ManagedEVPPKey pkey;

switch (key->key_type_) {
switch (type) {
case kKeyTypeSecret:
CHECK_EQ(args.Length(), 1);
CHECK(args[0]->IsArrayBufferView());
key->InitSecret(args[0].As<ArrayBufferView>());
CHECK_EQ(args.Length(), 2);
CHECK(args[1]->IsArrayBufferView());
key->data_.reset(
KeyObjectData::CreateSecret(args[1].As<ArrayBufferView>()));
break;
case kKeyTypePublic:
CHECK_EQ(args.Length(), 3);
CHECK_EQ(args.Length(), 4);

offset = 0;
offset = 1;
pkey = GetPublicOrPrivateKeyFromJs(args, &offset);
if (!pkey)
return;
key->InitPublic(pkey);
key->data_.reset(KeyObjectData::CreateAsymmetric(type, pkey));
break;
case kKeyTypePrivate:
CHECK_EQ(args.Length(), 4);
CHECK_EQ(args.Length(), 5);

offset = 0;
offset = 1;
pkey = GetPrivateKeyFromJs(args, &offset, false);
if (!pkey)
return;
key->InitPrivate(pkey);
key->data_.reset(KeyObjectData::CreateAsymmetric(type, pkey));
break;
default:
CHECK(false);
}
}

void KeyObjectHandle::InitSecret(Local<ArrayBufferView> abv) {
CHECK_EQ(this->key_type_, kKeyTypeSecret);

size_t key_len = abv->ByteLength();
char* mem = MallocOpenSSL<char>(key_len);
abv->CopyContents(mem, key_len);
this->symmetric_key_ = std::unique_ptr<char, std::function<void(char*)>>(mem,
[key_len](char* p) {
OPENSSL_clear_free(p, key_len);
});
this->symmetric_key_len_ = key_len;
}

void KeyObjectHandle::InitPublic(const ManagedEVPPKey& pkey) {
CHECK_EQ(this->key_type_, kKeyTypePublic);
CHECK(pkey);
this->asymmetric_key_ = pkey;
}

void KeyObjectHandle::InitPrivate(const ManagedEVPPKey& pkey) {
CHECK_EQ(this->key_type_, kKeyTypePrivate);
CHECK(pkey);
this->asymmetric_key_ = pkey;
}

Local<Value> KeyObjectHandle::GetAsymmetricKeyType() const {
CHECK_NE(this->key_type_, kKeyTypeSecret);
switch (EVP_PKEY_id(this->asymmetric_key_.get())) {
const ManagedEVPPKey& key = data_->GetAsymmetricKey();
switch (EVP_PKEY_id(key.get())) {
case EVP_PKEY_RSA:
return env()->crypto_rsa_string();
case EVP_PKEY_RSA_PSS:
Expand Down Expand Up @@ -3384,24 +3383,27 @@ void KeyObjectHandle::GetSymmetricKeySize(
const FunctionCallbackInfo<Value>& args) {
KeyObjectHandle* key;
ASSIGN_OR_RETURN_UNWRAP(&key, args.Holder());
args.GetReturnValue().Set(static_cast<uint32_t>(key->GetSymmetricKeySize()));
args.GetReturnValue().Set(
static_cast<uint32_t>(key->Data()->GetSymmetricKeySize()));
}

void KeyObjectHandle::Export(const FunctionCallbackInfo<Value>& args) {
KeyObjectHandle* key;
ASSIGN_OR_RETURN_UNWRAP(&key, args.Holder());

KeyType type = key->Data()->GetKeyType();

MaybeLocal<Value> result;
if (key->key_type_ == kKeyTypeSecret) {
if (type == kKeyTypeSecret) {
result = key->ExportSecretKey();
} else if (key->key_type_ == kKeyTypePublic) {
} else if (type == kKeyTypePublic) {
unsigned int offset = 0;
PublicKeyEncodingConfig config =
GetPublicKeyEncodingFromJs(args, &offset, kKeyContextExport);
CHECK_EQ(offset, static_cast<unsigned int>(args.Length()));
result = key->ExportPublicKey(config);
} else {
CHECK_EQ(key->key_type_, kKeyTypePrivate);
CHECK_EQ(type, kKeyTypePrivate);
unsigned int offset = 0;
NonCopyableMaybe<PrivateKeyEncodingConfig> config =
GetPrivateKeyEncodingFromJs(args, &offset, kKeyContextExport);
Expand All @@ -3416,18 +3418,19 @@ void KeyObjectHandle::Export(const FunctionCallbackInfo<Value>& args) {
}

Local<Value> KeyObjectHandle::ExportSecretKey() const {
return Buffer::Copy(env(), symmetric_key_.get(), symmetric_key_len_)
.ToLocalChecked();
const char* buf = data_->GetSymmetricKey();
unsigned int len = data_->GetSymmetricKeySize();
return Buffer::Copy(env(), buf, len).ToLocalChecked();
}

MaybeLocal<Value> KeyObjectHandle::ExportPublicKey(
const PublicKeyEncodingConfig& config) const {
return WritePublicKey(env(), asymmetric_key_.get(), config);
return WritePublicKey(env(), data_->GetAsymmetricKey().get(), config);
}

MaybeLocal<Value> KeyObjectHandle::ExportPrivateKey(
const PrivateKeyEncodingConfig& config) const {
return WritePrivateKey(env(), asymmetric_key_.get(), config);
return WritePrivateKey(env(), data_->GetAsymmetricKey().get(), config);
}

void NativeKeyObject::New(const FunctionCallbackInfo<Value>& args) {
Expand Down Expand Up @@ -6772,13 +6775,13 @@ void StatelessDiffieHellman(const FunctionCallbackInfo<Value>& 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->GetKeyType(), kKeyTypePrivate);
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->GetKeyType(), kKeyTypeSecret);
CHECK_NE(their_key_object->Data()->GetKeyType(), kKeyTypeSecret);

ManagedEVPPKey our_key = our_key_object->GetAsymmetricKey();
ManagedEVPPKey their_key = their_key_object->GetAsymmetricKey();
ManagedEVPPKey our_key = our_key_object->Data()->GetAsymmetricKey();
ManagedEVPPKey their_key = their_key_object->Data()->GetAsymmetricKey();

AllocatedBuffer out = StatelessDiffieHellman(env, our_key, their_key);
if (out.size() == 0)
Expand Down
40 changes: 24 additions & 16 deletions src/node_crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,27 @@ class ManagedEVPPKey {
EVPKeyPointer pkey_;
};

class KeyObjectData {
public:
static KeyObjectData* CreateSecret(v8::Local<v8::ArrayBufferView> abv);
static KeyObjectData* CreateAsymmetric(KeyType type,
const ManagedEVPPKey& pkey);

KeyType GetKeyType() const;

// These functions allow unprotected access to the raw key material and should
// only be used to implement cryptograohic operations requiring the key.
ManagedEVPPKey GetAsymmetricKey() const;
const char* GetSymmetricKey() const;
size_t GetSymmetricKeySize() const;

private:
KeyType key_type_;
std::unique_ptr<char, std::function<void(char*)>> symmetric_key_;
unsigned int symmetric_key_len_;
ManagedEVPPKey asymmetric_key_;
};

class KeyObjectHandle : public BaseObject {
public:
static v8::Local<v8::Function> Initialize(Environment* env,
Expand All @@ -421,21 +442,12 @@ class KeyObjectHandle : public BaseObject {
SET_MEMORY_INFO_NAME(KeyObjectHandle)
SET_SELF_SIZE(KeyObjectHandle)

KeyType GetKeyType() const;

// These functions allow unprotected access to the raw key material and should
// only be used to implement cryptograohic operations requiring the key.
ManagedEVPPKey GetAsymmetricKey() const;
const char* GetSymmetricKey() const;
size_t GetSymmetricKeySize() const;
const KeyObjectData* Data();

protected:
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);

static void Init(const v8::FunctionCallbackInfo<v8::Value>& args);
void InitSecret(v8::Local<v8::ArrayBufferView> abv);
void InitPublic(const ManagedEVPPKey& pkey);
void InitPrivate(const ManagedEVPPKey& pkey);

static void GetAsymmetricKeyType(
const v8::FunctionCallbackInfo<v8::Value>& args);
Expand All @@ -452,14 +464,10 @@ class KeyObjectHandle : public BaseObject {
const PrivateKeyEncodingConfig& config) const;

KeyObjectHandle(Environment* env,
v8::Local<v8::Object> wrap,
KeyType key_type);
v8::Local<v8::Object> wrap);

private:
const KeyType key_type_;
std::unique_ptr<char, std::function<void(char*)>> symmetric_key_;
unsigned int symmetric_key_len_;
ManagedEVPPKey asymmetric_key_;
std::unique_ptr<KeyObjectData> data_;
};

class NativeKeyObject : public BaseObject {
Expand Down

0 comments on commit 48a8339

Please sign in to comment.