GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_hmac.cc Lines: 122 151 80.8 %
Date: 2022-09-21 04:23:13 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::Isolate;
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
166
Hmac::Hmac(Environment* env, Local<Object> wrap)
31
    : BaseObject(env, wrap),
32
166
      ctx_(nullptr) {
33
166
  MakeWeak();
34
166
}
35
36
void Hmac::MemoryInfo(MemoryTracker* tracker) const {
37
  tracker->TrackFieldWithSize("context", ctx_ ? kSizeOf_HMAC_CTX : 0);
38
}
39
40
784
void Hmac::Initialize(Environment* env, Local<Object> target) {
41
784
  Isolate* isolate = env->isolate();
42
784
  Local<FunctionTemplate> t = NewFunctionTemplate(isolate, New);
43
44
1568
  t->InstanceTemplate()->SetInternalFieldCount(
45
      Hmac::kInternalFieldCount);
46
784
  t->Inherit(BaseObject::GetConstructorTemplate(env));
47
48
784
  SetProtoMethod(isolate, t, "init", HmacInit);
49
784
  SetProtoMethod(isolate, t, "update", HmacUpdate);
50
784
  SetProtoMethod(isolate, t, "digest", HmacDigest);
51
52
784
  SetConstructorFunction(env->context(), target, "Hmac", t);
53
54
784
  HmacJob::Initialize(env, target);
55
784
}
56
57
5527
void Hmac::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
58
5527
  registry->Register(New);
59
5527
  registry->Register(HmacInit);
60
5527
  registry->Register(HmacUpdate);
61
5527
  registry->Register(HmacDigest);
62
5527
  HmacJob::RegisterExternalReferences(registry);
63
5527
}
64
65
166
void Hmac::New(const FunctionCallbackInfo<Value>& args) {
66
166
  Environment* env = Environment::GetCurrent(args);
67
166
  new Hmac(env, args.This());
68
166
}
69
70
166
void Hmac::HmacInit(const char* hash_type, const char* key, int key_len) {
71
166
  HandleScope scope(env()->isolate());
72
73
166
  const EVP_MD* md = EVP_get_digestbyname(hash_type);
74
166
  if (md == nullptr)
75
1
    return THROW_ERR_CRYPTO_INVALID_DIGEST(
76
1
        env(), "Invalid digest: %s", hash_type);
77
165
  if (key_len == 0) {
78
8
    key = "";
79
  }
80
165
  ctx_.reset(HMAC_CTX_new());
81

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

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

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

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

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

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

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

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