xref: /third_party/node/src/crypto/crypto_hkdf.cc (revision 1cb0ef41)
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