GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_hmac.cc Lines: 120 149 80.5 %
Date: 2022-06-14 04:15:41 Branches: 50 95 52.6 %

Line Branch Exec Source
1
#include "crypto/crypto_hmac.h"
2
#include "async_wrap-inl.h"
3
#include "base_object-inl.h"
4
#include "crypto/crypto_keys.h"
5
#include "crypto/crypto_sig.h"
6
#include "crypto/crypto_util.h"
7
#include "env-inl.h"
8
#include "memory_tracker-inl.h"
9
#include "node_buffer.h"
10
#include "string_bytes.h"
11
#include "threadpoolwork-inl.h"
12
#include "v8.h"
13
14
namespace node {
15
16
using v8::FunctionCallbackInfo;
17
using v8::FunctionTemplate;
18
using v8::HandleScope;
19
using v8::Just;
20
using v8::Local;
21
using v8::Maybe;
22
using v8::MaybeLocal;
23
using v8::Nothing;
24
using v8::Object;
25
using v8::Uint32;
26
using v8::Value;
27
28
namespace crypto {
29
164
Hmac::Hmac(Environment* env, Local<Object> wrap)
30
    : BaseObject(env, wrap),
31
164
      ctx_(nullptr) {
32
164
  MakeWeak();
33
164
}
34
35
void Hmac::MemoryInfo(MemoryTracker* tracker) const {
36
  tracker->TrackFieldWithSize("context", ctx_ ? kSizeOf_HMAC_CTX : 0);
37
}
38
39
848
void Hmac::Initialize(Environment* env, Local<Object> target) {
40
848
  Local<FunctionTemplate> t = env->NewFunctionTemplate(New);
41
42
1696
  t->InstanceTemplate()->SetInternalFieldCount(
43
      Hmac::kInternalFieldCount);
44
848
  t->Inherit(BaseObject::GetConstructorTemplate(env));
45
46
848
  env->SetProtoMethod(t, "init", HmacInit);
47
848
  env->SetProtoMethod(t, "update", HmacUpdate);
48
848
  env->SetProtoMethod(t, "digest", HmacDigest);
49
50
848
  env->SetConstructorFunction(target, "Hmac", t);
51
52
848
  HmacJob::Initialize(env, target);
53
848
}
54
55
5205
void Hmac::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
56
5205
  registry->Register(New);
57
5205
  registry->Register(HmacInit);
58
5205
  registry->Register(HmacUpdate);
59
5205
  registry->Register(HmacDigest);
60
5205
  HmacJob::RegisterExternalReferences(registry);
61
5205
}
62
63
164
void Hmac::New(const FunctionCallbackInfo<Value>& args) {
64
164
  Environment* env = Environment::GetCurrent(args);
65
164
  new Hmac(env, args.This());
66
164
}
67
68
164
void Hmac::HmacInit(const char* hash_type, const char* key, int key_len) {
69
164
  HandleScope scope(env()->isolate());
70
71
164
  const EVP_MD* md = EVP_get_digestbyname(hash_type);
72
164
  if (md == nullptr)
73
1
    return THROW_ERR_CRYPTO_INVALID_DIGEST(env());
74
163
  if (key_len == 0) {
75
6
    key = "";
76
  }
77
163
  ctx_.reset(HMAC_CTX_new());
78

163
  if (!ctx_ || !HMAC_Init_ex(ctx_.get(), key, key_len, md, nullptr)) {
79
    ctx_.reset();
80
    return ThrowCryptoError(env(), ERR_get_error());
81
  }
82
}
83
84
164
void Hmac::HmacInit(const FunctionCallbackInfo<Value>& args) {
85
  Hmac* hmac;
86
164
  ASSIGN_OR_RETURN_UNWRAP(&hmac, args.Holder());
87
164
  Environment* env = hmac->env();
88
89
328
  const node::Utf8Value hash_type(env->isolate(), args[0]);
90
328
  ByteSource key = ByteSource::FromSecretKeyBytes(env, args[1]);
91
164
  hmac->HmacInit(*hash_type, key.data<char>(), key.size());
92
}
93
94
162
bool Hmac::HmacUpdate(const char* data, size_t len) {
95

162
  return ctx_ && HMAC_Update(ctx_.get(),
96
                             reinterpret_cast<const unsigned char*>(data),
97
324
                             len) == 1;
98
}
99
100
162
void Hmac::HmacUpdate(const FunctionCallbackInfo<Value>& args) {
101
162
  Decode<Hmac>(args, [](Hmac* hmac, const FunctionCallbackInfo<Value>& args,
102
162
                        const char* data, size_t size) {
103
162
    Environment* env = Environment::GetCurrent(args);
104
162
    if (UNLIKELY(size > INT_MAX))
105
      return THROW_ERR_OUT_OF_RANGE(env, "data is too long");
106
162
    bool r = hmac->HmacUpdate(data, size);
107
324
    args.GetReturnValue().Set(r);
108
  });
109
162
}
110
111
157
void Hmac::HmacDigest(const FunctionCallbackInfo<Value>& args) {
112
157
  Environment* env = Environment::GetCurrent(args);
113
114
  Hmac* hmac;
115
157
  ASSIGN_OR_RETURN_UNWRAP(&hmac, args.Holder());
116
117
157
  enum encoding encoding = BUFFER;
118
157
  if (args.Length() >= 1) {
119
129
    encoding = ParseEncoding(env->isolate(), args[0], BUFFER);
120
  }
121
122
  unsigned char md_value[EVP_MAX_MD_SIZE];
123
157
  unsigned int md_len = 0;
124
125
157
  if (hmac->ctx_) {
126
157
    bool ok = HMAC_Final(hmac->ctx_.get(), md_value, &md_len);
127
157
    hmac->ctx_.reset();
128
157
    if (!ok) {
129
      return ThrowCryptoError(env, ERR_get_error(), "Failed to finalize HMAC");
130
    }
131
  }
132
133
  Local<Value> error;
134
  MaybeLocal<Value> rc =
135
      StringBytes::Encode(env->isolate(),
136
                          reinterpret_cast<const char*>(md_value),
137
                          md_len,
138
                          encoding,
139
157
                          &error);
140
157
  if (rc.IsEmpty()) {
141
    CHECK(!error.IsEmpty());
142
    env->isolate()->ThrowException(error);
143
    return;
144
  }
145
471
  args.GetReturnValue().Set(rc.FromMaybe(Local<Value>()));
146
}
147
148
102
HmacConfig::HmacConfig(HmacConfig&& other) noexcept
149
102
    : job_mode(other.job_mode),
150
102
      mode(other.mode),
151
102
      key(std::move(other.key)),
152
102
      data(std::move(other.data)),
153
102
      signature(std::move(other.signature)),
154
102
      digest(other.digest) {}
155
156
HmacConfig& HmacConfig::operator=(HmacConfig&& other) noexcept {
157
  if (&other == this) return *this;
158
  this->~HmacConfig();
159
  return *new (this) HmacConfig(std::move(other));
160
}
161
162
void HmacConfig::MemoryInfo(MemoryTracker* tracker) const {
163
  tracker->TrackField("key", key.get());
164
  // If the job is sync, then the HmacConfig does not own the data
165
  if (job_mode == kCryptoJobAsync) {
166
    tracker->TrackFieldWithSize("data", data.size());
167
    tracker->TrackFieldWithSize("signature", signature.size());
168
  }
169
}
170
171
102
Maybe<bool> HmacTraits::AdditionalConfig(
172
    CryptoJobMode mode,
173
    const FunctionCallbackInfo<Value>& args,
174
    unsigned int offset,
175
    HmacConfig* params) {
176
102
  Environment* env = Environment::GetCurrent(args);
177
178
102
  params->job_mode = mode;
179
180

204
  CHECK(args[offset]->IsUint32());  // SignConfiguration::Mode
181
102
  params->mode =
182
306
    static_cast<SignConfiguration::Mode>(args[offset].As<Uint32>()->Value());
183
184

306
  CHECK(args[offset + 1]->IsString());  // Hash
185

204
  CHECK(args[offset + 2]->IsObject());  // Key
186
187
306
  Utf8Value digest(env->isolate(), args[offset + 1]);
188
102
  params->digest = EVP_get_digestbyname(*digest);
189
102
  if (params->digest == nullptr) {
190
    THROW_ERR_CRYPTO_INVALID_DIGEST(env);
191
    return Nothing<bool>();
192
  }
193
194
  KeyObjectHandle* key;
195

204
  ASSIGN_OR_RETURN_UNWRAP(&key, args[offset + 2], Nothing<bool>());
196
102
  params->key = key->Data();
197
198
306
  ArrayBufferOrViewContents<char> data(args[offset + 3]);
199
102
  if (UNLIKELY(!data.CheckSizeInt32())) {
200
    THROW_ERR_OUT_OF_RANGE(env, "data is too big");
201
    return Nothing<bool>();
202
  }
203
  params->data = mode == kCryptoJobAsync
204
204
      ? data.ToCopy()
205
102
      : data.ToByteSource();
206
207

306
  if (!args[offset + 4]->IsUndefined()) {
208
148
    ArrayBufferOrViewContents<char> signature(args[offset + 4]);
209
74
    if (UNLIKELY(!signature.CheckSizeInt32())) {
210
      THROW_ERR_OUT_OF_RANGE(env, "signature is too big");
211
      return Nothing<bool>();
212
    }
213
    params->signature = mode == kCryptoJobAsync
214
148
        ? signature.ToCopy()
215
74
        : signature.ToByteSource();
216
  }
217
218
102
  return Just(true);
219
}
220
221
102
bool HmacTraits::DeriveBits(
222
    Environment* env,
223
    const HmacConfig& params,
224
    ByteSource* out) {
225
204
  HMACCtxPointer ctx(HMAC_CTX_new());
226
227

204
  if (!ctx ||
228
102
      !HMAC_Init_ex(
229
          ctx.get(),
230
102
          params.key->GetSymmetricKey(),
231
102
          params.key->GetSymmetricKeySize(),
232
102
          params.digest,
233
          nullptr)) {
234
    return false;
235
  }
236
237
102
  if (!HMAC_Update(
238
          ctx.get(),
239
          params.data.data<unsigned char>(),
240
          params.data.size())) {
241
    return false;
242
  }
243
244
204
  ByteSource::Builder buf(EVP_MAX_MD_SIZE);
245
  unsigned int len;
246
247
102
  if (!HMAC_Final(ctx.get(), buf.data<unsigned char>(), &len)) {
248
    return false;
249
  }
250
251
102
  *out = std::move(buf).release(len);
252
253
102
  return true;
254
}
255
256
102
Maybe<bool> HmacTraits::EncodeOutput(
257
    Environment* env,
258
    const HmacConfig& params,
259
    ByteSource* out,
260
    Local<Value>* result) {
261
102
  switch (params.mode) {
262
28
    case SignConfiguration::kSign:
263
28
      *result = out->ToArrayBuffer(env);
264
28
      break;
265
74
    case SignConfiguration::kVerify:
266
120
      *result =
267
148
          out->size() > 0 && out->size() == params.signature.size() &&
268
66
                  memcmp(out->data(), params.signature.data(), out->size()) == 0
269
46
              ? v8::True(env->isolate())
270
28
              : v8::False(env->isolate());
271
74
      break;
272
    default:
273
      UNREACHABLE();
274
  }
275
102
  return Just(!result->IsEmpty());
276
}
277
278
}  // namespace crypto
279
}  // namespace node