• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "crypto/crypto_hkdf.h"
2 #include "async_wrap-inl.h"
3 #include "base_object-inl.h"
4 #include "crypto/crypto_keys.h"
5 #include "env-inl.h"
6 #include "memory_tracker-inl.h"
7 #include "threadpoolwork-inl.h"
8 #include "v8.h"
9 
10 namespace node {
11 
12 using v8::FunctionCallbackInfo;
13 using v8::Just;
14 using v8::Maybe;
15 using v8::Nothing;
16 using v8::Uint32;
17 using v8::Value;
18 
19 namespace crypto {
HKDFConfig(HKDFConfig && other)20 HKDFConfig::HKDFConfig(HKDFConfig&& other) noexcept
21     : mode(other.mode),
22       length(other.length),
23       digest(other.digest),
24       key(other.key),
25       salt(std::move(other.salt)),
26       info(std::move(other.info)) {}
27 
operator =(HKDFConfig && other)28 HKDFConfig& HKDFConfig::operator=(HKDFConfig&& other) noexcept {
29   if (&other == this) return *this;
30   this->~HKDFConfig();
31   return *new (this) HKDFConfig(std::move(other));
32 }
33 
EncodeOutput(Environment * env,const HKDFConfig & params,ByteSource * out,v8::Local<v8::Value> * result)34 Maybe<bool> HKDFTraits::EncodeOutput(
35     Environment* env,
36     const HKDFConfig& params,
37     ByteSource* out,
38     v8::Local<v8::Value>* result) {
39   *result = out->ToArrayBuffer(env);
40   return Just(!result->IsEmpty());
41 }
42 
AdditionalConfig(CryptoJobMode mode,const FunctionCallbackInfo<Value> & args,unsigned int offset,HKDFConfig * params)43 Maybe<bool> HKDFTraits::AdditionalConfig(
44     CryptoJobMode mode,
45     const FunctionCallbackInfo<Value>& args,
46     unsigned int offset,
47     HKDFConfig* params) {
48   Environment* env = Environment::GetCurrent(args);
49 
50   params->mode = mode;
51 
52   CHECK(args[offset]->IsString());  // Hash
53   CHECK(args[offset + 1]->IsObject());  // Key
54   CHECK(IsAnyByteSource(args[offset + 2]));  // Salt
55   CHECK(IsAnyByteSource(args[offset + 3]));  // Info
56   CHECK(args[offset + 4]->IsUint32());  // Length
57 
58   Utf8Value hash(env->isolate(), args[offset]);
59   params->digest = EVP_get_digestbyname(*hash);
60   if (params->digest == nullptr) {
61     THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *hash);
62     return Nothing<bool>();
63   }
64 
65   KeyObjectHandle* key;
66   ASSIGN_OR_RETURN_UNWRAP(&key, args[offset + 1], Nothing<bool>());
67   params->key = key->Data();
68 
69   ArrayBufferOrViewContents<char> salt(args[offset + 2]);
70   ArrayBufferOrViewContents<char> info(args[offset + 3]);
71 
72   if (UNLIKELY(!salt.CheckSizeInt32())) {
73     THROW_ERR_OUT_OF_RANGE(env, "salt is too big");
74     return Nothing<bool>();
75   }
76   if (UNLIKELY(!info.CheckSizeInt32())) {
77     THROW_ERR_OUT_OF_RANGE(env, "info is too big");
78     return Nothing<bool>();
79   }
80 
81   params->salt = mode == kCryptoJobAsync
82       ? salt.ToCopy()
83       : salt.ToByteSource();
84 
85   params->info = mode == kCryptoJobAsync
86       ? info.ToCopy()
87       : info.ToByteSource();
88 
89   params->length = args[offset + 4].As<Uint32>()->Value();
90   // HKDF-Expand computes up to 255 HMAC blocks, each having as many bits as the
91   // output of the hash function. 255 is a hard limit because HKDF appends an
92   // 8-bit counter to each HMAC'd message, starting at 1.
93   constexpr size_t kMaxDigestMultiplier = 255;
94   size_t max_length = EVP_MD_size(params->digest) * kMaxDigestMultiplier;
95   if (params->length > max_length) {
96     THROW_ERR_CRYPTO_INVALID_KEYLEN(env);
97     return Nothing<bool>();
98   }
99 
100   return Just(true);
101 }
102 
DeriveBits(Environment * env,const HKDFConfig & params,ByteSource * out)103 bool HKDFTraits::DeriveBits(
104     Environment* env,
105     const HKDFConfig& params,
106     ByteSource* out) {
107   EVPKeyCtxPointer ctx =
108       EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr));
109   if (!ctx || !EVP_PKEY_derive_init(ctx.get()) ||
110       !EVP_PKEY_CTX_set_hkdf_md(ctx.get(), params.digest) ||
111       !EVP_PKEY_CTX_add1_hkdf_info(
112           ctx.get(), params.info.data<unsigned char>(), params.info.size())) {
113     return false;
114   }
115 
116   // TODO(panva): Once support for OpenSSL 1.1.1 is dropped the whole
117   // of HKDFTraits::DeriveBits can be refactored to use
118   // EVP_KDF which does handle zero length key.
119 
120   std::string_view salt;
121   if (params.salt.size() != 0) {
122     salt = {params.salt.data<char>(), params.salt.size()};
123   } else {
124     static const char default_salt[EVP_MAX_MD_SIZE] = {0};
125     salt = {default_salt, static_cast<unsigned>(EVP_MD_size(params.digest))};
126   }
127 
128   // We do not use EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND and instead implement
129   // the extraction step ourselves because EVP_PKEY_derive does not handle
130   // zero-length keys, which are required for Web Crypto.
131   unsigned char pseudorandom_key[EVP_MAX_MD_SIZE];
132   unsigned int prk_len = sizeof(pseudorandom_key);
133   if (HMAC(
134           params.digest,
135           salt.data(),
136           salt.size(),
137           reinterpret_cast<const unsigned char*>(params.key->GetSymmetricKey()),
138           params.key->GetSymmetricKeySize(),
139           pseudorandom_key,
140           &prk_len) == nullptr) {
141     return false;
142   }
143   if (!EVP_PKEY_CTX_hkdf_mode(ctx.get(), EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) ||
144       !EVP_PKEY_CTX_set1_hkdf_key(ctx.get(), pseudorandom_key, prk_len)) {
145     return false;
146   }
147 
148   size_t length = params.length;
149   ByteSource::Builder buf(length);
150   if (EVP_PKEY_derive(ctx.get(), buf.data<unsigned char>(), &length) <= 0)
151     return false;
152 
153   *out = std::move(buf).release();
154   return true;
155 }
156 
MemoryInfo(MemoryTracker * tracker) const157 void HKDFConfig::MemoryInfo(MemoryTracker* tracker) const {
158   tracker->TrackField("key", key);
159   // If the job is sync, then the HKDFConfig does not own the data
160   if (mode == kCryptoJobAsync) {
161     tracker->TrackFieldWithSize("salt", salt.size());
162     tracker->TrackFieldWithSize("info", info.size());
163   }
164 }
165 
166 }  // namespace crypto
167 }  // namespace node
168