src: allow not materializing ArrayBuffers from C++
Where appropriate, use a helper that wraps around `ArrayBufferView::Buffer()` or `ArrayBufferView::CopyContents()` rather than `Buffer::Data()`, as that may help to avoid materializing the underlying `ArrayBuffer` when reading small typed arrays from C++. This allows keeping the performance benefits of the faster creation of heap-allocated small typed arrays in many cases. PR-URL: https://github.com/nodejs/node/pull/26301 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
b42dcb0eeb
commit
31975bbc88
@ -57,6 +57,7 @@ namespace crypto {
|
||||
using node::THROW_ERR_TLS_INVALID_PROTOCOL_METHOD;
|
||||
|
||||
using v8::Array;
|
||||
using v8::ArrayBufferView;
|
||||
using v8::Boolean;
|
||||
using v8::ConstructorBehavior;
|
||||
using v8::Context;
|
||||
@ -539,8 +540,9 @@ static BIOPointer LoadBIO(Environment* env, Local<Value> v) {
|
||||
return NodeBIO::NewFixed(*s, s.length());
|
||||
}
|
||||
|
||||
if (Buffer::HasInstance(v)) {
|
||||
return NodeBIO::NewFixed(Buffer::Data(v), Buffer::Length(v));
|
||||
if (v->IsArrayBufferView()) {
|
||||
ArrayBufferViewContents<char> buf(v.As<ArrayBufferView>());
|
||||
return NodeBIO::NewFixed(buf.data(), buf.length());
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@ -1135,9 +1137,10 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
if (args.Length() >= 2) {
|
||||
THROW_AND_RETURN_IF_NOT_BUFFER(env, args[1], "Pass phrase");
|
||||
size_t passlen = Buffer::Length(args[1]);
|
||||
Local<ArrayBufferView> abv = args[1].As<ArrayBufferView>();
|
||||
size_t passlen = abv->ByteLength();
|
||||
pass.resize(passlen + 1);
|
||||
memcpy(pass.data(), Buffer::Data(args[1]), passlen);
|
||||
abv->CopyContents(pass.data(), passlen);
|
||||
pass[passlen] = '\0';
|
||||
}
|
||||
|
||||
@ -1261,15 +1264,16 @@ void SecureContext::SetTicketKeys(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
|
||||
THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Ticket keys");
|
||||
ArrayBufferViewContents<char> buf(args[0].As<ArrayBufferView>());
|
||||
|
||||
if (Buffer::Length(args[0]) != 48) {
|
||||
if (buf.length() != 48) {
|
||||
return THROW_ERR_INVALID_ARG_VALUE(
|
||||
env, "Ticket keys length must be 48 bytes");
|
||||
}
|
||||
|
||||
memcpy(wrap->ticket_key_name_, Buffer::Data(args[0]), 16);
|
||||
memcpy(wrap->ticket_key_hmac_, Buffer::Data(args[0]) + 16, 16);
|
||||
memcpy(wrap->ticket_key_aes_, Buffer::Data(args[0]) + 32, 16);
|
||||
memcpy(wrap->ticket_key_name_, buf.data(), 16);
|
||||
memcpy(wrap->ticket_key_hmac_, buf.data() + 16, 16);
|
||||
memcpy(wrap->ticket_key_aes_, buf.data() + 32, 16);
|
||||
|
||||
args.GetReturnValue().Set(true);
|
||||
#endif // !def(OPENSSL_NO_TLSEXT) && def(SSL_CTX_get_tlsext_ticket_keys)
|
||||
@ -1349,29 +1353,29 @@ int SecureContext::TicketKeyCallback(SSL* ssl,
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(name, Buffer::Data(name_val), kTicketPartSize);
|
||||
memcpy(iv, Buffer::Data(iv_val), kTicketPartSize);
|
||||
name_val.As<ArrayBufferView>()->CopyContents(name, kTicketPartSize);
|
||||
iv_val.As<ArrayBufferView>()->CopyContents(iv, kTicketPartSize);
|
||||
}
|
||||
|
||||
ArrayBufferViewContents<unsigned char> hmac_buf(hmac);
|
||||
HMAC_Init_ex(hctx,
|
||||
Buffer::Data(hmac),
|
||||
Buffer::Length(hmac),
|
||||
hmac_buf.data(),
|
||||
hmac_buf.length(),
|
||||
EVP_sha256(),
|
||||
nullptr);
|
||||
|
||||
const unsigned char* aes_key =
|
||||
reinterpret_cast<unsigned char*>(Buffer::Data(aes));
|
||||
ArrayBufferViewContents<unsigned char> aes_key(aes.As<ArrayBufferView>());
|
||||
if (enc) {
|
||||
EVP_EncryptInit_ex(ectx,
|
||||
EVP_aes_128_cbc(),
|
||||
nullptr,
|
||||
aes_key,
|
||||
aes_key.data(),
|
||||
iv);
|
||||
} else {
|
||||
EVP_DecryptInit_ex(ectx,
|
||||
EVP_aes_128_cbc(),
|
||||
nullptr,
|
||||
aes_key,
|
||||
aes_key.data(),
|
||||
iv);
|
||||
}
|
||||
|
||||
@ -2094,13 +2098,10 @@ void SSLWrap<Base>::SetSession(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
|
||||
THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Session");
|
||||
size_t slen = Buffer::Length(args[0]);
|
||||
std::vector<char> sbuf(slen);
|
||||
if (char* p = Buffer::Data(args[0]))
|
||||
sbuf.assign(p, p + slen);
|
||||
ArrayBufferViewContents<unsigned char> sbuf(args[0].As<ArrayBufferView>());
|
||||
|
||||
const unsigned char* p = reinterpret_cast<const unsigned char*>(sbuf.data());
|
||||
SSLSessionPointer sess(d2i_SSL_SESSION(nullptr, &p, slen));
|
||||
const unsigned char* p = sbuf.data();
|
||||
SSLSessionPointer sess(d2i_SSL_SESSION(nullptr, &p, sbuf.length()));
|
||||
|
||||
if (sess == nullptr)
|
||||
return;
|
||||
@ -2118,11 +2119,10 @@ void SSLWrap<Base>::LoadSession(const FunctionCallbackInfo<Value>& args) {
|
||||
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());
|
||||
|
||||
if (args.Length() >= 1 && Buffer::HasInstance(args[0])) {
|
||||
ssize_t slen = Buffer::Length(args[0]);
|
||||
char* sbuf = Buffer::Data(args[0]);
|
||||
ArrayBufferViewContents<unsigned char> sbuf(args[0]);
|
||||
|
||||
const unsigned char* p = reinterpret_cast<unsigned char*>(sbuf);
|
||||
SSL_SESSION* sess = d2i_SSL_SESSION(nullptr, &p, slen);
|
||||
const unsigned char* p = sbuf.data();
|
||||
SSL_SESSION* sess = d2i_SSL_SESSION(nullptr, &p, sbuf.length());
|
||||
|
||||
// Setup next session and move hello to the BIO buffer
|
||||
w->next_sess_.reset(sess);
|
||||
@ -2204,7 +2204,7 @@ void SSLWrap<Base>::SetOCSPResponse(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "OCSP response");
|
||||
|
||||
w->ocsp_response_.Reset(args.GetIsolate(), args[0].As<Object>());
|
||||
w->ocsp_response_.Reset(args.GetIsolate(), args[0].As<ArrayBufferView>());
|
||||
}
|
||||
|
||||
|
||||
@ -2403,12 +2403,10 @@ int SSLWrap<Base>::SelectALPNCallback(SSL* s,
|
||||
w->object()->GetPrivate(
|
||||
env->context(),
|
||||
env->alpn_buffer_private_symbol()).ToLocalChecked();
|
||||
CHECK(Buffer::HasInstance(alpn_buffer));
|
||||
const unsigned char* alpn_protos =
|
||||
reinterpret_cast<const unsigned char*>(Buffer::Data(alpn_buffer));
|
||||
unsigned alpn_protos_len = Buffer::Length(alpn_buffer);
|
||||
ArrayBufferViewContents<unsigned char> alpn_protos(alpn_buffer);
|
||||
int status = SSL_select_next_proto(const_cast<unsigned char**>(out), outlen,
|
||||
alpn_protos, alpn_protos_len, in, inlen);
|
||||
alpn_protos.data(), alpn_protos.length(),
|
||||
in, inlen);
|
||||
// According to 3.2. Protocol Selection of RFC7301, fatal
|
||||
// no_application_protocol alert shall be sent but OpenSSL 1.0.2 does not
|
||||
// support it yet. See
|
||||
@ -2446,10 +2444,9 @@ void SSLWrap<Base>::SetALPNProtocols(const FunctionCallbackInfo<Value>& args) {
|
||||
return env->ThrowTypeError("Must give a Buffer as first argument");
|
||||
|
||||
if (w->is_client()) {
|
||||
const unsigned char* alpn_protos =
|
||||
reinterpret_cast<const unsigned char*>(Buffer::Data(args[0]));
|
||||
unsigned alpn_protos_len = Buffer::Length(args[0]);
|
||||
int r = SSL_set_alpn_protos(w->ssl_.get(), alpn_protos, alpn_protos_len);
|
||||
ArrayBufferViewContents<unsigned char> alpn_protos(args[0]);
|
||||
int r = SSL_set_alpn_protos(
|
||||
w->ssl_.get(), alpn_protos.data(), alpn_protos.length());
|
||||
CHECK_EQ(r, 0);
|
||||
} else {
|
||||
CHECK(
|
||||
@ -2493,14 +2490,13 @@ int SSLWrap<Base>::TLSExtStatusCallback(SSL* s, void* arg) {
|
||||
if (w->ocsp_response_.IsEmpty())
|
||||
return SSL_TLSEXT_ERR_NOACK;
|
||||
|
||||
Local<Object> obj = PersistentToLocal::Default(env->isolate(),
|
||||
w->ocsp_response_);
|
||||
char* resp = Buffer::Data(obj);
|
||||
size_t len = Buffer::Length(obj);
|
||||
Local<ArrayBufferView> obj = PersistentToLocal::Default(env->isolate(),
|
||||
w->ocsp_response_);
|
||||
size_t len = obj->ByteLength();
|
||||
|
||||
// OpenSSL takes control of the pointer after accepting it
|
||||
unsigned char* data = MallocOpenSSL<unsigned char>(len);
|
||||
memcpy(data, resp, len);
|
||||
obj->CopyContents(data, len);
|
||||
|
||||
if (!SSL_set_tlsext_status_ocsp_resp(s, data, len))
|
||||
OPENSSL_free(data);
|
||||
@ -2998,10 +2994,12 @@ ByteSource ByteSource::FromString(Environment* env, Local<String> str,
|
||||
}
|
||||
|
||||
ByteSource ByteSource::FromBuffer(Local<Value> buffer, bool ntc) {
|
||||
size_t size = Buffer::Length(buffer);
|
||||
CHECK(buffer->IsArrayBufferView());
|
||||
Local<ArrayBufferView> abv = buffer.As<ArrayBufferView>();
|
||||
size_t size = abv->ByteLength();
|
||||
if (ntc) {
|
||||
char* data = MallocOpenSSL<char>(size + 1);
|
||||
memcpy(data, Buffer::Data(buffer), size);
|
||||
abv->CopyContents(data, size);
|
||||
data[size] = 0;
|
||||
return Allocated(data, size);
|
||||
}
|
||||
@ -3404,7 +3402,8 @@ void KeyObject::Init(const FunctionCallbackInfo<Value>& args) {
|
||||
switch (key->key_type_) {
|
||||
case kKeyTypeSecret:
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
key->InitSecret(Buffer::Data(args[0]), Buffer::Length(args[0]));
|
||||
CHECK(args[0]->IsArrayBufferView());
|
||||
key->InitSecret(args[0].As<ArrayBufferView>());
|
||||
break;
|
||||
case kKeyTypePublic:
|
||||
CHECK_EQ(args.Length(), 3);
|
||||
@ -3429,11 +3428,12 @@ void KeyObject::Init(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
}
|
||||
|
||||
void KeyObject::InitSecret(const char* key, size_t key_len) {
|
||||
void KeyObject::InitSecret(v8::Local<v8::ArrayBufferView> abv) {
|
||||
CHECK_EQ(this->key_type_, kKeyTypeSecret);
|
||||
|
||||
size_t key_len = abv->ByteLength();
|
||||
char* mem = MallocOpenSSL<char>(key_len);
|
||||
memcpy(mem, key, 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);
|
||||
@ -3643,8 +3643,7 @@ void CipherBase::Init(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK_GE(args.Length(), 3);
|
||||
|
||||
const node::Utf8Value cipher_type(args.GetIsolate(), args[0]);
|
||||
const char* key_buf = Buffer::Data(args[1]);
|
||||
ssize_t key_buf_len = Buffer::Length(args[1]);
|
||||
ArrayBufferViewContents<char> key_buf(args[1]);
|
||||
|
||||
// Don't assign to cipher->auth_tag_len_ directly; the value might not
|
||||
// represent a valid length at this point.
|
||||
@ -3656,7 +3655,7 @@ void CipherBase::Init(const FunctionCallbackInfo<Value>& args) {
|
||||
auth_tag_len = kNoAuthTagLength;
|
||||
}
|
||||
|
||||
cipher->Init(*cipher_type, key_buf, key_buf_len, auth_tag_len);
|
||||
cipher->Init(*cipher_type, key_buf.data(), key_buf.length(), auth_tag_len);
|
||||
}
|
||||
|
||||
void CipherBase::InitIv(const char* cipher_type,
|
||||
@ -3712,14 +3711,12 @@ void CipherBase::InitIv(const FunctionCallbackInfo<Value>& args) {
|
||||
const node::Utf8Value cipher_type(env->isolate(), args[0]);
|
||||
const ByteSource key = GetSecretKeyBytes(env, args[1]);
|
||||
|
||||
ssize_t iv_len;
|
||||
const unsigned char* iv_buf;
|
||||
if (args[2]->IsNull()) {
|
||||
iv_buf = nullptr;
|
||||
iv_len = -1;
|
||||
} else {
|
||||
iv_buf = reinterpret_cast<unsigned char*>(Buffer::Data(args[2]));
|
||||
iv_len = Buffer::Length(args[2]);
|
||||
ArrayBufferViewContents<unsigned char> iv_buf;
|
||||
ssize_t iv_len = -1;
|
||||
if (!args[2]->IsNull()) {
|
||||
CHECK(args[2]->IsArrayBufferView());
|
||||
iv_buf.Read(args[2].As<ArrayBufferView>());
|
||||
iv_len = iv_buf.length();
|
||||
}
|
||||
|
||||
// Don't assign to cipher->auth_tag_len_ directly; the value might not
|
||||
@ -3735,7 +3732,7 @@ void CipherBase::InitIv(const FunctionCallbackInfo<Value>& args) {
|
||||
cipher->InitIv(*cipher_type,
|
||||
reinterpret_cast<const unsigned char*>(key.get()),
|
||||
key.size(),
|
||||
iv_buf,
|
||||
iv_buf.data(),
|
||||
iv_len,
|
||||
auth_tag_len);
|
||||
}
|
||||
@ -3889,7 +3886,8 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK_LE(cipher->auth_tag_len_, sizeof(cipher->auth_tag_));
|
||||
|
||||
memset(cipher->auth_tag_, 0, sizeof(cipher->auth_tag_));
|
||||
memcpy(cipher->auth_tag_, Buffer::Data(args[0]), cipher->auth_tag_len_);
|
||||
args[0].As<ArrayBufferView>()->CopyContents(
|
||||
cipher->auth_tag_, cipher->auth_tag_len_);
|
||||
|
||||
args.GetReturnValue().Set(true);
|
||||
}
|
||||
@ -3953,9 +3951,9 @@ void CipherBase::SetAAD(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK_EQ(args.Length(), 2);
|
||||
CHECK(args[1]->IsInt32());
|
||||
int plaintext_len = args[1].As<Int32>()->Value();
|
||||
ArrayBufferViewContents<char> buf(args[0]);
|
||||
|
||||
bool b = cipher->SetAAD(Buffer::Data(args[0]), Buffer::Length(args[0]),
|
||||
plaintext_len);
|
||||
bool b = cipher->SetAAD(buf.data(), buf.length(), plaintext_len);
|
||||
args.GetReturnValue().Set(b); // Possibly report invalid state failure
|
||||
}
|
||||
|
||||
@ -4028,9 +4026,8 @@ void CipherBase::Update(const FunctionCallbackInfo<Value>& args) {
|
||||
return;
|
||||
r = cipher->Update(decoder.out(), decoder.size(), &out);
|
||||
} else {
|
||||
char* buf = Buffer::Data(args[0]);
|
||||
size_t buflen = Buffer::Length(args[0]);
|
||||
r = cipher->Update(buf, buflen, &out);
|
||||
ArrayBufferViewContents<char> buf(args[0]);
|
||||
r = cipher->Update(buf.data(), buf.length(), &out);
|
||||
}
|
||||
|
||||
if (r != kSuccess) {
|
||||
@ -4213,10 +4210,8 @@ void Hmac::HmacUpdate(const FunctionCallbackInfo<Value>& args) {
|
||||
r = hmac->HmacUpdate(decoder.out(), decoder.size());
|
||||
}
|
||||
} else {
|
||||
CHECK(args[0]->IsArrayBufferView());
|
||||
char* buf = Buffer::Data(args[0]);
|
||||
size_t buflen = Buffer::Length(args[0]);
|
||||
r = hmac->HmacUpdate(buf, buflen);
|
||||
ArrayBufferViewContents<char> buf(args[0]);
|
||||
r = hmac->HmacUpdate(buf.data(), buf.length());
|
||||
}
|
||||
|
||||
args.GetReturnValue().Set(r);
|
||||
@ -4324,9 +4319,8 @@ void Hash::HashUpdate(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
r = hash->HashUpdate(decoder.out(), decoder.size());
|
||||
} else if (args[0]->IsArrayBufferView()) {
|
||||
char* buf = Buffer::Data(args[0]);
|
||||
size_t buflen = Buffer::Length(args[0]);
|
||||
r = hash->HashUpdate(buf, buflen);
|
||||
ArrayBufferViewContents<char> buf(args[0].As<ArrayBufferView>());
|
||||
r = hash->HashUpdate(buf.data(), buf.length());
|
||||
}
|
||||
|
||||
args.GetReturnValue().Set(r);
|
||||
@ -4487,9 +4481,8 @@ void Sign::SignUpdate(const FunctionCallbackInfo<Value>& args) {
|
||||
ASSIGN_OR_RETURN_UNWRAP(&sign, args.Holder());
|
||||
|
||||
Error err;
|
||||
char* buf = Buffer::Data(args[0]);
|
||||
size_t buflen = Buffer::Length(args[0]);
|
||||
err = sign->Update(buf, buflen);
|
||||
ArrayBufferViewContents<char> buf(args[0]);
|
||||
err = sign->Update(buf.data(), buf.length());
|
||||
|
||||
sign->CheckThrow(err);
|
||||
}
|
||||
@ -4634,9 +4627,8 @@ void Verify::VerifyUpdate(const FunctionCallbackInfo<Value>& args) {
|
||||
ASSIGN_OR_RETURN_UNWRAP(&verify, args.Holder());
|
||||
|
||||
Error err;
|
||||
char* buf = Buffer::Data(args[0]);
|
||||
size_t buflen = Buffer::Length(args[0]);
|
||||
err = verify->Update(buf, buflen);
|
||||
ArrayBufferViewContents<char> buf(args[0]);
|
||||
err = verify->Update(buf.data(), buf.length());
|
||||
|
||||
verify->CheckThrow(err);
|
||||
}
|
||||
@ -4688,8 +4680,7 @@ void Verify::VerifyFinal(const FunctionCallbackInfo<Value>& args) {
|
||||
if (!pkey)
|
||||
return;
|
||||
|
||||
char* hbuf = Buffer::Data(args[offset]);
|
||||
ssize_t hlen = Buffer::Length(args[offset]);
|
||||
ArrayBufferViewContents<char> hbuf(args[offset]);
|
||||
|
||||
CHECK(args[offset + 1]->IsInt32());
|
||||
int padding = args[offset + 1].As<Int32>()->Value();
|
||||
@ -4698,8 +4689,8 @@ void Verify::VerifyFinal(const FunctionCallbackInfo<Value>& args) {
|
||||
int salt_len = args[offset + 2].As<Int32>()->Value();
|
||||
|
||||
bool verify_result;
|
||||
Error err = verify->VerifyFinal(pkey, hbuf, hlen, padding, salt_len,
|
||||
&verify_result);
|
||||
Error err = verify->VerifyFinal(pkey, hbuf.data(), hbuf.length(), padding,
|
||||
salt_len, &verify_result);
|
||||
if (err != kSignOk)
|
||||
return verify->CheckThrow(err);
|
||||
args.GetReturnValue().Set(verify_result);
|
||||
@ -4753,8 +4744,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo<Value>& args) {
|
||||
return;
|
||||
|
||||
THROW_AND_RETURN_IF_NOT_BUFFER(env, args[offset], "Data");
|
||||
char* buf = Buffer::Data(args[offset]);
|
||||
ssize_t len = Buffer::Length(args[offset]);
|
||||
ArrayBufferViewContents<unsigned char> buf(args[offset]);
|
||||
|
||||
uint32_t padding;
|
||||
if (!args[offset + 1]->Uint32Value(env->context()).To(&padding)) return;
|
||||
@ -4767,8 +4757,8 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo<Value>& args) {
|
||||
env,
|
||||
pkey,
|
||||
padding,
|
||||
reinterpret_cast<const unsigned char*>(buf),
|
||||
len,
|
||||
buf.data(),
|
||||
buf.length(),
|
||||
&out);
|
||||
|
||||
if (!r)
|
||||
@ -4906,15 +4896,15 @@ void DiffieHellman::New(const FunctionCallbackInfo<Value>& args) {
|
||||
args[1].As<Int32>()->Value());
|
||||
}
|
||||
} else {
|
||||
ArrayBufferViewContents<char> arg0(args[0]);
|
||||
if (args[1]->IsInt32()) {
|
||||
initialized = diffieHellman->Init(Buffer::Data(args[0]),
|
||||
Buffer::Length(args[0]),
|
||||
initialized = diffieHellman->Init(arg0.data(),
|
||||
arg0.length(),
|
||||
args[1].As<Int32>()->Value());
|
||||
} else {
|
||||
initialized = diffieHellman->Init(Buffer::Data(args[0]),
|
||||
Buffer::Length(args[0]),
|
||||
Buffer::Data(args[1]),
|
||||
Buffer::Length(args[1]));
|
||||
ArrayBufferViewContents<char> arg1(args[1]);
|
||||
initialized = diffieHellman->Init(arg0.data(), arg0.length(),
|
||||
arg1.data(), arg1.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5017,10 +5007,8 @@ void DiffieHellman::ComputeSecret(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
|
||||
THROW_AND_RETURN_IF_NOT_BUFFER(env, args[0], "Other party's public key");
|
||||
BignumPointer key(BN_bin2bn(
|
||||
reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
|
||||
Buffer::Length(args[0]),
|
||||
nullptr));
|
||||
ArrayBufferViewContents<unsigned char> key_buf(args[0].As<ArrayBufferView>());
|
||||
BignumPointer key(BN_bin2bn(key_buf.data(), key_buf.length(), nullptr));
|
||||
|
||||
AllocatedBuffer ret = env->AllocateManaged(DH_size(diffieHellman->dh_.get()));
|
||||
|
||||
@ -5087,9 +5075,9 @@ void DiffieHellman::SetKey(const FunctionCallbackInfo<Value>& args,
|
||||
return THROW_ERR_INVALID_ARG_TYPE(env, errmsg);
|
||||
}
|
||||
|
||||
ArrayBufferViewContents<unsigned char> buf(args[0].As<ArrayBufferView>());
|
||||
BIGNUM* num =
|
||||
BN_bin2bn(reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
|
||||
Buffer::Length(args[0]), nullptr);
|
||||
BN_bin2bn(buf.data(), buf.length(), nullptr);
|
||||
CHECK_NOT_NULL(num);
|
||||
CHECK_EQ(1, set_field(dh->dh_.get(), num));
|
||||
}
|
||||
@ -5188,8 +5176,7 @@ void ECDH::GenerateKeys(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
ECPointPointer ECDH::BufferToPoint(Environment* env,
|
||||
const EC_GROUP* group,
|
||||
char* data,
|
||||
size_t len) {
|
||||
Local<Value> buf) {
|
||||
int r;
|
||||
|
||||
ECPointPointer pub(EC_POINT_new(group));
|
||||
@ -5198,11 +5185,12 @@ ECPointPointer ECDH::BufferToPoint(Environment* env,
|
||||
return pub;
|
||||
}
|
||||
|
||||
ArrayBufferViewContents<unsigned char> input(buf);
|
||||
r = EC_POINT_oct2point(
|
||||
group,
|
||||
pub.get(),
|
||||
reinterpret_cast<unsigned char*>(data),
|
||||
len,
|
||||
input.data(),
|
||||
input.length(),
|
||||
nullptr);
|
||||
if (!r)
|
||||
return ECPointPointer();
|
||||
@ -5227,8 +5215,7 @@ void ECDH::ComputeSecret(const FunctionCallbackInfo<Value>& args) {
|
||||
ECPointPointer pub(
|
||||
ECDH::BufferToPoint(env,
|
||||
ecdh->group_,
|
||||
Buffer::Data(args[0]),
|
||||
Buffer::Length(args[0])));
|
||||
args[0]));
|
||||
if (!pub) {
|
||||
args.GetReturnValue().Set(
|
||||
FIXED_ONE_BYTE_STRING(env->isolate(),
|
||||
@ -5360,8 +5347,7 @@ void ECDH::SetPublicKey(const FunctionCallbackInfo<Value>& args) {
|
||||
ECPointPointer pub(
|
||||
ECDH::BufferToPoint(env,
|
||||
ecdh->group_,
|
||||
Buffer::Data(args[0].As<Object>()),
|
||||
Buffer::Length(args[0].As<Object>())));
|
||||
args[0]));
|
||||
if (!pub)
|
||||
return env->ThrowError("Failed to convert Buffer to EC_POINT");
|
||||
|
||||
@ -5427,8 +5413,10 @@ void CryptoJob::Run(std::unique_ptr<CryptoJob> job, Local<Value> wrap) {
|
||||
|
||||
|
||||
inline void CopyBuffer(Local<Value> buf, std::vector<char>* vec) {
|
||||
CHECK(buf->IsArrayBufferView());
|
||||
vec->clear();
|
||||
if (auto p = Buffer::Data(buf)) vec->assign(p, p + Buffer::Length(buf));
|
||||
vec->resize(buf.As<ArrayBufferView>()->ByteLength());
|
||||
buf.As<ArrayBufferView>()->CopyContents(vec->data(), vec->size());
|
||||
}
|
||||
|
||||
|
||||
@ -6033,14 +6021,13 @@ bool VerifySpkac(const char* data, unsigned int len) {
|
||||
void VerifySpkac(const FunctionCallbackInfo<Value>& args) {
|
||||
bool verify_result = false;
|
||||
|
||||
size_t length = Buffer::Length(args[0]);
|
||||
if (length == 0)
|
||||
return args.GetReturnValue().Set(verify_result);
|
||||
ArrayBufferViewContents<char> input(args[0]);
|
||||
if (input.length() == 0)
|
||||
return args.GetReturnValue().SetEmptyString();
|
||||
|
||||
char* data = Buffer::Data(args[0]);
|
||||
CHECK_NOT_NULL(data);
|
||||
CHECK_NOT_NULL(input.data());
|
||||
|
||||
verify_result = VerifySpkac(data, length);
|
||||
verify_result = VerifySpkac(input.data(), input.length());
|
||||
|
||||
args.GetReturnValue().Set(verify_result);
|
||||
}
|
||||
@ -6075,15 +6062,15 @@ AllocatedBuffer ExportPublicKey(Environment* env,
|
||||
void ExportPublicKey(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
|
||||
size_t length = Buffer::Length(args[0]);
|
||||
if (length == 0)
|
||||
ArrayBufferViewContents<char> input(args[0]);
|
||||
if (input.length() == 0)
|
||||
return args.GetReturnValue().SetEmptyString();
|
||||
|
||||
char* data = Buffer::Data(args[0]);
|
||||
CHECK_NOT_NULL(data);
|
||||
CHECK_NOT_NULL(input.data());
|
||||
|
||||
size_t pkey_size;
|
||||
AllocatedBuffer pkey = ExportPublicKey(env, data, length, &pkey_size);
|
||||
AllocatedBuffer pkey =
|
||||
ExportPublicKey(env, input.data(), input.length(), &pkey_size);
|
||||
if (pkey.data() == nullptr)
|
||||
return args.GetReturnValue().SetEmptyString();
|
||||
|
||||
@ -6130,8 +6117,9 @@ void ConvertKey(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
|
||||
CHECK_EQ(args.Length(), 3);
|
||||
CHECK(args[0]->IsArrayBufferView());
|
||||
|
||||
size_t len = Buffer::Length(args[0]);
|
||||
size_t len = args[0].As<ArrayBufferView>()->ByteLength();
|
||||
if (len == 0)
|
||||
return args.GetReturnValue().SetEmptyString();
|
||||
|
||||
@ -6149,8 +6137,7 @@ void ConvertKey(const FunctionCallbackInfo<Value>& args) {
|
||||
ECPointPointer pub(
|
||||
ECDH::BufferToPoint(env,
|
||||
group.get(),
|
||||
Buffer::Data(args[0]),
|
||||
len));
|
||||
args[0]));
|
||||
|
||||
if (pub == nullptr)
|
||||
return env->ThrowError("Failed to convert Buffer to EC_POINT");
|
||||
|
@ -333,7 +333,7 @@ class SSLWrap {
|
||||
|
||||
ClientHelloParser hello_parser_;
|
||||
|
||||
Persistent<v8::Object> ocsp_response_;
|
||||
Persistent<v8::ArrayBufferView> ocsp_response_;
|
||||
Persistent<v8::Value> sni_context_;
|
||||
|
||||
friend class SecureContext;
|
||||
@ -462,7 +462,7 @@ class KeyObject : public BaseObject {
|
||||
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
static void Init(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
void InitSecret(const char* key, size_t key_len);
|
||||
void InitSecret(v8::Local<v8::ArrayBufferView> abv);
|
||||
void InitPublic(const ManagedEVPPKey& pkey);
|
||||
void InitPrivate(const ManagedEVPPKey& pkey);
|
||||
|
||||
@ -803,8 +803,7 @@ class ECDH : public BaseObject {
|
||||
static void Initialize(Environment* env, v8::Local<v8::Object> target);
|
||||
static ECPointPointer BufferToPoint(Environment* env,
|
||||
const EC_GROUP* group,
|
||||
char* data,
|
||||
size_t len);
|
||||
v8::Local<v8::Value> buf);
|
||||
|
||||
// TODO(joyeecheung): track the memory used by OpenSSL types
|
||||
SET_NO_MEMORY_INFO()
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace node {
|
||||
|
||||
using v8::ArrayBuffer;
|
||||
using v8::ArrayBufferView;
|
||||
using v8::Boolean;
|
||||
using v8::Context;
|
||||
using v8::Float64Array;
|
||||
@ -2483,7 +2484,7 @@ void Http2Session::Request(const FunctionCallbackInfo<Value>& args) {
|
||||
// state of the Http2Session, it's simply a notification.
|
||||
void Http2Session::Goaway(uint32_t code,
|
||||
int32_t lastStreamID,
|
||||
uint8_t* data,
|
||||
const uint8_t* data,
|
||||
size_t len) {
|
||||
if (IsDestroyed())
|
||||
return;
|
||||
@ -2508,16 +2509,13 @@ void Http2Session::Goaway(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
uint32_t code = args[0]->Uint32Value(context).ToChecked();
|
||||
int32_t lastStreamID = args[1]->Int32Value(context).ToChecked();
|
||||
Local<Value> opaqueData = args[2];
|
||||
uint8_t* data = nullptr;
|
||||
size_t length = 0;
|
||||
ArrayBufferViewContents<uint8_t> opaque_data;
|
||||
|
||||
if (Buffer::HasInstance(opaqueData)) {
|
||||
data = reinterpret_cast<uint8_t*>(Buffer::Data(opaqueData));
|
||||
length = Buffer::Length(opaqueData);
|
||||
if (args[2]->IsArrayBufferView()) {
|
||||
opaque_data.Read(args[2].As<ArrayBufferView>());
|
||||
}
|
||||
|
||||
session->Goaway(code, lastStreamID, data, length);
|
||||
session->Goaway(code, lastStreamID, opaque_data.data(), opaque_data.length());
|
||||
}
|
||||
|
||||
// Update accounting of data chunks. This is used primarily to manage timeout
|
||||
@ -2771,10 +2769,10 @@ void Http2Session::Ping(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
// A PING frame may have exactly 8 bytes of payload data. If not provided,
|
||||
// then the current hrtime will be used as the payload.
|
||||
uint8_t* payload = nullptr;
|
||||
if (Buffer::HasInstance(args[0])) {
|
||||
payload = reinterpret_cast<uint8_t*>(Buffer::Data(args[0]));
|
||||
CHECK_EQ(Buffer::Length(args[0]), 8);
|
||||
ArrayBufferViewContents<uint8_t, 8> payload;
|
||||
if (args[0]->IsArrayBufferView()) {
|
||||
payload.Read(args[0].As<ArrayBufferView>());
|
||||
CHECK_EQ(payload.length(), 8);
|
||||
}
|
||||
|
||||
Local<Object> obj;
|
||||
@ -2799,7 +2797,7 @@ void Http2Session::Ping(const FunctionCallbackInfo<Value>& args) {
|
||||
// the callback will be invoked and a notification sent out to JS land. The
|
||||
// notification will include the duration of the ping, allowing the round
|
||||
// trip to be measured.
|
||||
ping->Send(payload);
|
||||
ping->Send(payload.data());
|
||||
args.GetReturnValue().Set(true);
|
||||
}
|
||||
|
||||
@ -2871,7 +2869,7 @@ Http2Session::Http2Ping::Http2Ping(Http2Session* session, Local<Object> obj)
|
||||
session_(session),
|
||||
startTime_(uv_hrtime()) {}
|
||||
|
||||
void Http2Session::Http2Ping::Send(uint8_t* payload) {
|
||||
void Http2Session::Http2Ping::Send(const uint8_t* payload) {
|
||||
uint8_t data[8];
|
||||
if (payload == nullptr) {
|
||||
memcpy(&data, &startTime_, arraysize(data));
|
||||
|
@ -699,7 +699,8 @@ class Http2Session : public AsyncWrap, public StreamListener {
|
||||
void Close(uint32_t code = NGHTTP2_NO_ERROR,
|
||||
bool socket_closed = false);
|
||||
void Consume(Local<External> external);
|
||||
void Goaway(uint32_t code, int32_t lastStreamID, uint8_t* data, size_t len);
|
||||
void Goaway(uint32_t code, int32_t lastStreamID,
|
||||
const uint8_t* data, size_t len);
|
||||
void AltSvc(int32_t id,
|
||||
uint8_t* origin,
|
||||
size_t origin_len,
|
||||
@ -1089,7 +1090,7 @@ class Http2Session::Http2Ping : public AsyncWrap {
|
||||
SET_MEMORY_INFO_NAME(Http2Ping)
|
||||
SET_SELF_SIZE(Http2Ping)
|
||||
|
||||
void Send(uint8_t* payload);
|
||||
void Send(const uint8_t* payload);
|
||||
void Done(bool ack, const uint8_t* payload = nullptr);
|
||||
|
||||
private:
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "string_decoder-inl.h"
|
||||
|
||||
using v8::Array;
|
||||
using v8::ArrayBufferView;
|
||||
using v8::Context;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::Integer;
|
||||
@ -252,9 +253,13 @@ void DecodeData(const FunctionCallbackInfo<Value>& args) {
|
||||
StringDecoder* decoder =
|
||||
reinterpret_cast<StringDecoder*>(Buffer::Data(args[0]));
|
||||
CHECK_NOT_NULL(decoder);
|
||||
size_t nread = Buffer::Length(args[1]);
|
||||
|
||||
CHECK(args[1]->IsArrayBufferView());
|
||||
ArrayBufferViewContents<char> content(args[1].As<ArrayBufferView>());
|
||||
size_t length = content.length();
|
||||
|
||||
MaybeLocal<String> ret =
|
||||
decoder->DecodeData(args.GetIsolate(), Buffer::Data(args[1]), &nread);
|
||||
decoder->DecodeData(args.GetIsolate(), content.data(), &length);
|
||||
if (!ret.IsEmpty())
|
||||
args.GetReturnValue().Set(ret.ToLocalChecked());
|
||||
}
|
||||
|
@ -466,6 +466,32 @@ SlicedArguments::SlicedArguments(
|
||||
(*this)[i] = args[i + start];
|
||||
}
|
||||
|
||||
template <typename T, size_t S>
|
||||
ArrayBufferViewContents<T, S>::ArrayBufferViewContents(
|
||||
v8::Local<v8::Value> value) {
|
||||
CHECK(value->IsArrayBufferView());
|
||||
Read(value.As<v8::ArrayBufferView>());
|
||||
}
|
||||
|
||||
template <typename T, size_t S>
|
||||
ArrayBufferViewContents<T, S>::ArrayBufferViewContents(
|
||||
v8::Local<v8::ArrayBufferView> abv) {
|
||||
Read(abv);
|
||||
}
|
||||
|
||||
template <typename T, size_t S>
|
||||
void ArrayBufferViewContents<T, S>::Read(v8::Local<v8::ArrayBufferView> abv) {
|
||||
static_assert(sizeof(T) == 1, "Only supports one-byte data at the moment");
|
||||
length_ = abv->ByteLength();
|
||||
if (length_ > sizeof(stack_storage_) || abv->HasBuffer()) {
|
||||
data_ = static_cast<T*>(abv->Buffer()->GetContents().Data()) +
|
||||
abv->ByteOffset();
|
||||
} else {
|
||||
abv->CopyContents(stack_storage_, sizeof(stack_storage_));
|
||||
data_ = stack_storage_;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
namespace node {
|
||||
|
||||
using v8::ArrayBufferView;
|
||||
using v8::Isolate;
|
||||
using v8::Local;
|
||||
using v8::String;
|
||||
@ -89,11 +90,11 @@ BufferValue::BufferValue(Isolate* isolate, Local<Value> value) {
|
||||
|
||||
if (value->IsString()) {
|
||||
MakeUtf8String(isolate, value, this);
|
||||
} else if (Buffer::HasInstance(value)) {
|
||||
const size_t len = Buffer::Length(value);
|
||||
} else if (value->IsArrayBufferView()) {
|
||||
const size_t len = value.As<ArrayBufferView>()->ByteLength();
|
||||
// Leave place for the terminating '\0' byte.
|
||||
AllocateSufficientStorage(len + 1);
|
||||
memcpy(out(), Buffer::Data(value), len);
|
||||
value.As<ArrayBufferView>()->CopyContents(out(), len);
|
||||
SetLengthAndZeroTerminate(len);
|
||||
} else {
|
||||
Invalidate();
|
||||
|
21
src/util.h
21
src/util.h
@ -417,6 +417,27 @@ class MaybeStackBuffer {
|
||||
T buf_st_[kStackStorageSize];
|
||||
};
|
||||
|
||||
// Provides access to an ArrayBufferView's storage, either the original,
|
||||
// or for small data, a copy of it. This object's lifetime is bound to the
|
||||
// original ArrayBufferView's lifetime.
|
||||
template <typename T, size_t kStackStorageSize = 64>
|
||||
class ArrayBufferViewContents {
|
||||
public:
|
||||
ArrayBufferViewContents() {}
|
||||
|
||||
explicit inline ArrayBufferViewContents(v8::Local<v8::Value> value);
|
||||
explicit inline ArrayBufferViewContents(v8::Local<v8::ArrayBufferView> abv);
|
||||
inline void Read(v8::Local<v8::ArrayBufferView> abv);
|
||||
|
||||
inline const T* data() const { return data_; }
|
||||
inline size_t length() const { return length_; }
|
||||
|
||||
private:
|
||||
T stack_storage_[kStackStorageSize];
|
||||
T* data_ = nullptr;
|
||||
size_t length_ = 0;
|
||||
};
|
||||
|
||||
class Utf8Value : public MaybeStackBuffer<char> {
|
||||
public:
|
||||
explicit Utf8Value(v8::Isolate* isolate, v8::Local<v8::Value> value);
|
||||
|
Loading…
x
Reference in New Issue
Block a user