blob: f6339b129baea6c17676e1747d812fae9830c168 [file] [edit]
#include "crypto/crypto_hkdf.h"
#include "crypto/crypto_keys.h"
#include "allocated_buffer-inl.h"
#include "async_wrap-inl.h"
#include "base_object-inl.h"
#include "env-inl.h"
#include "memory_tracker-inl.h"
#include "threadpoolwork-inl.h"
#include "v8.h"
namespace node {
using v8::FunctionCallbackInfo;
using v8::Just;
using v8::Maybe;
using v8::Nothing;
using v8::Uint32;
using v8::Value;
namespace crypto {
HKDFConfig::HKDFConfig(HKDFConfig&& other) noexcept
: mode(other.mode),
length(other.length),
digest(other.digest),
key(other.key),
salt(std::move(other.salt)),
info(std::move(other.info)) {}
HKDFConfig& HKDFConfig::operator=(HKDFConfig&& other) noexcept {
if (&other == this) return *this;
this->~HKDFConfig();
return *new (this) HKDFConfig(std::move(other));
}
Maybe<bool> HKDFTraits::EncodeOutput(
Environment* env,
const HKDFConfig& params,
ByteSource* out,
v8::Local<v8::Value>* result) {
*result = out->ToArrayBuffer(env);
return Just(!result->IsEmpty());
}
Maybe<bool> HKDFTraits::AdditionalConfig(
CryptoJobMode mode,
const FunctionCallbackInfo<Value>& args,
unsigned int offset,
HKDFConfig* params) {
Environment* env = Environment::GetCurrent(args);
params->mode = mode;
CHECK(args[offset]->IsString()); // Hash
CHECK(args[offset + 1]->IsObject()); // Key
CHECK(IsAnyByteSource(args[offset + 2])); // Salt
CHECK(IsAnyByteSource(args[offset + 3])); // Info
CHECK(args[offset + 4]->IsUint32()); // Length
Utf8Value hash(env->isolate(), args[offset]);
params->digest = EVP_get_digestbyname(*hash);
if (params->digest == nullptr) {
THROW_ERR_CRYPTO_INVALID_DIGEST(env);
return Nothing<bool>();
}
KeyObjectHandle* key;
ASSIGN_OR_RETURN_UNWRAP(&key, args[offset + 1], Nothing<bool>());
params->key = key->Data();
ArrayBufferOrViewContents<char> salt(args[offset + 2]);
ArrayBufferOrViewContents<char> info(args[offset + 3]);
if (UNLIKELY(!salt.CheckSizeInt32())) {
THROW_ERR_OUT_OF_RANGE(env, "salt is too big");
return Nothing<bool>();
}
if (UNLIKELY(!info.CheckSizeInt32())) {
THROW_ERR_OUT_OF_RANGE(env, "info is too big");
return Nothing<bool>();
}
params->salt = mode == kCryptoJobAsync
? salt.ToCopy()
: salt.ToByteSource();
params->info = mode == kCryptoJobAsync
? info.ToCopy()
: info.ToByteSource();
params->length = args[offset + 4].As<Uint32>()->Value();
size_t max_length = EVP_MD_size(params->digest) * kMaxDigestMultiplier;
if (params->length > max_length) {
THROW_ERR_CRYPTO_INVALID_KEYLEN(env);
return Nothing<bool>();
}
return Just(true);
}
bool HKDFTraits::DeriveBits(
Environment* env,
const HKDFConfig& params,
ByteSource* out) {
EVPKeyCtxPointer ctx =
EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr));
if (!ctx ||
!EVP_PKEY_derive_init(ctx.get()) ||
!EVP_PKEY_CTX_hkdf_mode(
ctx.get(), EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND) ||
!EVP_PKEY_CTX_set_hkdf_md(ctx.get(), params.digest) ||
!EVP_PKEY_CTX_set1_hkdf_salt(
ctx.get(),
params.salt.get(),
params.salt.size()) ||
!EVP_PKEY_CTX_set1_hkdf_key(
ctx.get(),
params.key->GetSymmetricKey(),
params.key->GetSymmetricKeySize()) ||
!EVP_PKEY_CTX_add1_hkdf_info(
ctx.get(),
params.info.get(),
params.info.size())) {
return false;
}
size_t length = params.length;
char* data = MallocOpenSSL<char>(length);
ByteSource buf = ByteSource::Allocated(data, length);
unsigned char* ptr = reinterpret_cast<unsigned char*>(data);
if (EVP_PKEY_derive(ctx.get(), ptr, &length) <= 0)
return false;
*out = std::move(buf);
return true;
}
void HKDFConfig::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackField("key", key);
// If the job is sync, then the HKDFConfig does not own the data
if (mode == kCryptoJobAsync) {
tracker->TrackFieldWithSize("salt", salt.size());
tracker->TrackFieldWithSize("info", info.size());
}
}
} // namespace crypto
} // namespace node