GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_hmac.cc Lines: 124 153 81.0 %
Date: 2022-04-17 04:15:01 Branches: 50 95 52.6 %

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

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

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

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

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

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

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

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

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