11cb0ef41Sopenharmony_ci#include "crypto/crypto_hkdf.h" 21cb0ef41Sopenharmony_ci#include "async_wrap-inl.h" 31cb0ef41Sopenharmony_ci#include "base_object-inl.h" 41cb0ef41Sopenharmony_ci#include "crypto/crypto_keys.h" 51cb0ef41Sopenharmony_ci#include "env-inl.h" 61cb0ef41Sopenharmony_ci#include "memory_tracker-inl.h" 71cb0ef41Sopenharmony_ci#include "threadpoolwork-inl.h" 81cb0ef41Sopenharmony_ci#include "v8.h" 91cb0ef41Sopenharmony_ci 101cb0ef41Sopenharmony_cinamespace node { 111cb0ef41Sopenharmony_ci 121cb0ef41Sopenharmony_ciusing v8::FunctionCallbackInfo; 131cb0ef41Sopenharmony_ciusing v8::Just; 141cb0ef41Sopenharmony_ciusing v8::Maybe; 151cb0ef41Sopenharmony_ciusing v8::Nothing; 161cb0ef41Sopenharmony_ciusing v8::Uint32; 171cb0ef41Sopenharmony_ciusing v8::Value; 181cb0ef41Sopenharmony_ci 191cb0ef41Sopenharmony_cinamespace crypto { 201cb0ef41Sopenharmony_ciHKDFConfig::HKDFConfig(HKDFConfig&& other) noexcept 211cb0ef41Sopenharmony_ci : mode(other.mode), 221cb0ef41Sopenharmony_ci length(other.length), 231cb0ef41Sopenharmony_ci digest(other.digest), 241cb0ef41Sopenharmony_ci key(other.key), 251cb0ef41Sopenharmony_ci salt(std::move(other.salt)), 261cb0ef41Sopenharmony_ci info(std::move(other.info)) {} 271cb0ef41Sopenharmony_ci 281cb0ef41Sopenharmony_ciHKDFConfig& HKDFConfig::operator=(HKDFConfig&& other) noexcept { 291cb0ef41Sopenharmony_ci if (&other == this) return *this; 301cb0ef41Sopenharmony_ci this->~HKDFConfig(); 311cb0ef41Sopenharmony_ci return *new (this) HKDFConfig(std::move(other)); 321cb0ef41Sopenharmony_ci} 331cb0ef41Sopenharmony_ci 341cb0ef41Sopenharmony_ciMaybe<bool> HKDFTraits::EncodeOutput( 351cb0ef41Sopenharmony_ci Environment* env, 361cb0ef41Sopenharmony_ci const HKDFConfig& params, 371cb0ef41Sopenharmony_ci ByteSource* out, 381cb0ef41Sopenharmony_ci v8::Local<v8::Value>* result) { 391cb0ef41Sopenharmony_ci *result = out->ToArrayBuffer(env); 401cb0ef41Sopenharmony_ci return Just(!result->IsEmpty()); 411cb0ef41Sopenharmony_ci} 421cb0ef41Sopenharmony_ci 431cb0ef41Sopenharmony_ciMaybe<bool> HKDFTraits::AdditionalConfig( 441cb0ef41Sopenharmony_ci CryptoJobMode mode, 451cb0ef41Sopenharmony_ci const FunctionCallbackInfo<Value>& args, 461cb0ef41Sopenharmony_ci unsigned int offset, 471cb0ef41Sopenharmony_ci HKDFConfig* params) { 481cb0ef41Sopenharmony_ci Environment* env = Environment::GetCurrent(args); 491cb0ef41Sopenharmony_ci 501cb0ef41Sopenharmony_ci params->mode = mode; 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_ci CHECK(args[offset]->IsString()); // Hash 531cb0ef41Sopenharmony_ci CHECK(args[offset + 1]->IsObject()); // Key 541cb0ef41Sopenharmony_ci CHECK(IsAnyByteSource(args[offset + 2])); // Salt 551cb0ef41Sopenharmony_ci CHECK(IsAnyByteSource(args[offset + 3])); // Info 561cb0ef41Sopenharmony_ci CHECK(args[offset + 4]->IsUint32()); // Length 571cb0ef41Sopenharmony_ci 581cb0ef41Sopenharmony_ci Utf8Value hash(env->isolate(), args[offset]); 591cb0ef41Sopenharmony_ci params->digest = EVP_get_digestbyname(*hash); 601cb0ef41Sopenharmony_ci if (params->digest == nullptr) { 611cb0ef41Sopenharmony_ci THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *hash); 621cb0ef41Sopenharmony_ci return Nothing<bool>(); 631cb0ef41Sopenharmony_ci } 641cb0ef41Sopenharmony_ci 651cb0ef41Sopenharmony_ci KeyObjectHandle* key; 661cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&key, args[offset + 1], Nothing<bool>()); 671cb0ef41Sopenharmony_ci params->key = key->Data(); 681cb0ef41Sopenharmony_ci 691cb0ef41Sopenharmony_ci ArrayBufferOrViewContents<char> salt(args[offset + 2]); 701cb0ef41Sopenharmony_ci ArrayBufferOrViewContents<char> info(args[offset + 3]); 711cb0ef41Sopenharmony_ci 721cb0ef41Sopenharmony_ci if (UNLIKELY(!salt.CheckSizeInt32())) { 731cb0ef41Sopenharmony_ci THROW_ERR_OUT_OF_RANGE(env, "salt is too big"); 741cb0ef41Sopenharmony_ci return Nothing<bool>(); 751cb0ef41Sopenharmony_ci } 761cb0ef41Sopenharmony_ci if (UNLIKELY(!info.CheckSizeInt32())) { 771cb0ef41Sopenharmony_ci THROW_ERR_OUT_OF_RANGE(env, "info is too big"); 781cb0ef41Sopenharmony_ci return Nothing<bool>(); 791cb0ef41Sopenharmony_ci } 801cb0ef41Sopenharmony_ci 811cb0ef41Sopenharmony_ci params->salt = mode == kCryptoJobAsync 821cb0ef41Sopenharmony_ci ? salt.ToCopy() 831cb0ef41Sopenharmony_ci : salt.ToByteSource(); 841cb0ef41Sopenharmony_ci 851cb0ef41Sopenharmony_ci params->info = mode == kCryptoJobAsync 861cb0ef41Sopenharmony_ci ? info.ToCopy() 871cb0ef41Sopenharmony_ci : info.ToByteSource(); 881cb0ef41Sopenharmony_ci 891cb0ef41Sopenharmony_ci params->length = args[offset + 4].As<Uint32>()->Value(); 901cb0ef41Sopenharmony_ci // HKDF-Expand computes up to 255 HMAC blocks, each having as many bits as the 911cb0ef41Sopenharmony_ci // output of the hash function. 255 is a hard limit because HKDF appends an 921cb0ef41Sopenharmony_ci // 8-bit counter to each HMAC'd message, starting at 1. 931cb0ef41Sopenharmony_ci constexpr size_t kMaxDigestMultiplier = 255; 941cb0ef41Sopenharmony_ci size_t max_length = EVP_MD_size(params->digest) * kMaxDigestMultiplier; 951cb0ef41Sopenharmony_ci if (params->length > max_length) { 961cb0ef41Sopenharmony_ci THROW_ERR_CRYPTO_INVALID_KEYLEN(env); 971cb0ef41Sopenharmony_ci return Nothing<bool>(); 981cb0ef41Sopenharmony_ci } 991cb0ef41Sopenharmony_ci 1001cb0ef41Sopenharmony_ci return Just(true); 1011cb0ef41Sopenharmony_ci} 1021cb0ef41Sopenharmony_ci 1031cb0ef41Sopenharmony_cibool HKDFTraits::DeriveBits( 1041cb0ef41Sopenharmony_ci Environment* env, 1051cb0ef41Sopenharmony_ci const HKDFConfig& params, 1061cb0ef41Sopenharmony_ci ByteSource* out) { 1071cb0ef41Sopenharmony_ci EVPKeyCtxPointer ctx = 1081cb0ef41Sopenharmony_ci EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr)); 1091cb0ef41Sopenharmony_ci if (!ctx || !EVP_PKEY_derive_init(ctx.get()) || 1101cb0ef41Sopenharmony_ci !EVP_PKEY_CTX_set_hkdf_md(ctx.get(), params.digest) || 1111cb0ef41Sopenharmony_ci !EVP_PKEY_CTX_add1_hkdf_info( 1121cb0ef41Sopenharmony_ci ctx.get(), params.info.data<unsigned char>(), params.info.size())) { 1131cb0ef41Sopenharmony_ci return false; 1141cb0ef41Sopenharmony_ci } 1151cb0ef41Sopenharmony_ci 1161cb0ef41Sopenharmony_ci // TODO(panva): Once support for OpenSSL 1.1.1 is dropped the whole 1171cb0ef41Sopenharmony_ci // of HKDFTraits::DeriveBits can be refactored to use 1181cb0ef41Sopenharmony_ci // EVP_KDF which does handle zero length key. 1191cb0ef41Sopenharmony_ci 1201cb0ef41Sopenharmony_ci std::string_view salt; 1211cb0ef41Sopenharmony_ci if (params.salt.size() != 0) { 1221cb0ef41Sopenharmony_ci salt = {params.salt.data<char>(), params.salt.size()}; 1231cb0ef41Sopenharmony_ci } else { 1241cb0ef41Sopenharmony_ci static const char default_salt[EVP_MAX_MD_SIZE] = {0}; 1251cb0ef41Sopenharmony_ci salt = {default_salt, static_cast<unsigned>(EVP_MD_size(params.digest))}; 1261cb0ef41Sopenharmony_ci } 1271cb0ef41Sopenharmony_ci 1281cb0ef41Sopenharmony_ci // We do not use EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND and instead implement 1291cb0ef41Sopenharmony_ci // the extraction step ourselves because EVP_PKEY_derive does not handle 1301cb0ef41Sopenharmony_ci // zero-length keys, which are required for Web Crypto. 1311cb0ef41Sopenharmony_ci unsigned char pseudorandom_key[EVP_MAX_MD_SIZE]; 1321cb0ef41Sopenharmony_ci unsigned int prk_len = sizeof(pseudorandom_key); 1331cb0ef41Sopenharmony_ci if (HMAC( 1341cb0ef41Sopenharmony_ci params.digest, 1351cb0ef41Sopenharmony_ci salt.data(), 1361cb0ef41Sopenharmony_ci salt.size(), 1371cb0ef41Sopenharmony_ci reinterpret_cast<const unsigned char*>(params.key->GetSymmetricKey()), 1381cb0ef41Sopenharmony_ci params.key->GetSymmetricKeySize(), 1391cb0ef41Sopenharmony_ci pseudorandom_key, 1401cb0ef41Sopenharmony_ci &prk_len) == nullptr) { 1411cb0ef41Sopenharmony_ci return false; 1421cb0ef41Sopenharmony_ci } 1431cb0ef41Sopenharmony_ci if (!EVP_PKEY_CTX_hkdf_mode(ctx.get(), EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) || 1441cb0ef41Sopenharmony_ci !EVP_PKEY_CTX_set1_hkdf_key(ctx.get(), pseudorandom_key, prk_len)) { 1451cb0ef41Sopenharmony_ci return false; 1461cb0ef41Sopenharmony_ci } 1471cb0ef41Sopenharmony_ci 1481cb0ef41Sopenharmony_ci size_t length = params.length; 1491cb0ef41Sopenharmony_ci ByteSource::Builder buf(length); 1501cb0ef41Sopenharmony_ci if (EVP_PKEY_derive(ctx.get(), buf.data<unsigned char>(), &length) <= 0) 1511cb0ef41Sopenharmony_ci return false; 1521cb0ef41Sopenharmony_ci 1531cb0ef41Sopenharmony_ci *out = std::move(buf).release(); 1541cb0ef41Sopenharmony_ci return true; 1551cb0ef41Sopenharmony_ci} 1561cb0ef41Sopenharmony_ci 1571cb0ef41Sopenharmony_civoid HKDFConfig::MemoryInfo(MemoryTracker* tracker) const { 1581cb0ef41Sopenharmony_ci tracker->TrackField("key", key); 1591cb0ef41Sopenharmony_ci // If the job is sync, then the HKDFConfig does not own the data 1601cb0ef41Sopenharmony_ci if (mode == kCryptoJobAsync) { 1611cb0ef41Sopenharmony_ci tracker->TrackFieldWithSize("salt", salt.size()); 1621cb0ef41Sopenharmony_ci tracker->TrackFieldWithSize("info", info.size()); 1631cb0ef41Sopenharmony_ci } 1641cb0ef41Sopenharmony_ci} 1651cb0ef41Sopenharmony_ci 1661cb0ef41Sopenharmony_ci} // namespace crypto 1671cb0ef41Sopenharmony_ci} // namespace node 168