GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/crypto/crypto_hash.cc Lines: 134 160 83.8 %
Date: 2020-12-12 04:11:07 Branches: 64 106 60.4 %

Line Branch Exec Source
1
#include "crypto/crypto_hash.h"
2
#include "allocated_buffer-inl.h"
3
#include "async_wrap-inl.h"
4
#include "base_object-inl.h"
5
#include "env-inl.h"
6
#include "memory_tracker-inl.h"
7
#include "string_bytes.h"
8
#include "threadpoolwork-inl.h"
9
#include "v8.h"
10
11
#include <cstdio>
12
13
namespace node {
14
15
using v8::FunctionCallbackInfo;
16
using v8::FunctionTemplate;
17
using v8::Just;
18
using v8::Local;
19
using v8::Maybe;
20
using v8::MaybeLocal;
21
using v8::Nothing;
22
using v8::Object;
23
using v8::Uint32;
24
using v8::Value;
25
26
namespace crypto {
27
5511
Hash::Hash(Environment* env, Local<Object> wrap) : BaseObject(env, wrap) {
28
5511
  MakeWeak();
29
5511
}
30
31
void Hash::MemoryInfo(MemoryTracker* tracker) const {
32
  tracker->TrackFieldWithSize("mdctx", mdctx_ ? kSizeOf_EVP_MD_CTX : 0);
33
  tracker->TrackFieldWithSize("md", digest_ ? md_len_ : 0);
34
}
35
36
7
void Hash::GetHashes(const FunctionCallbackInfo<Value>& args) {
37
7
  Environment* env = Environment::GetCurrent(args);
38
14
  CipherPushContext ctx(env);
39
7
  EVP_MD_do_all_sorted(array_push_back<EVP_MD>, &ctx);
40
21
  args.GetReturnValue().Set(ctx.ToJSArray());
41
7
}
42
43
656
void Hash::Initialize(Environment* env, Local<Object> target) {
44
656
  Local<FunctionTemplate> t = env->NewFunctionTemplate(New);
45
46
1968
  t->InstanceTemplate()->SetInternalFieldCount(
47
656
      Hash::kInternalFieldCount);
48
1312
  t->Inherit(BaseObject::GetConstructorTemplate(env));
49
50
656
  env->SetProtoMethod(t, "update", HashUpdate);
51
656
  env->SetProtoMethod(t, "digest", HashDigest);
52
53
1312
  target->Set(env->context(),
54
              FIXED_ONE_BYTE_STRING(env->isolate(), "Hash"),
55
3936
              t->GetFunction(env->context()).ToLocalChecked()).Check();
56
57
656
  env->SetMethodNoSideEffect(target, "getHashes", GetHashes);
58
59
656
  HashJob::Initialize(env, target);
60
656
}
61
62
5511
void Hash::New(const FunctionCallbackInfo<Value>& args) {
63
5511
  Environment* env = Environment::GetCurrent(args);
64
65
5511
  const Hash* orig = nullptr;
66
5511
  const EVP_MD* md = nullptr;
67
68
11022
  if (args[0]->IsObject()) {
69
13
    ASSIGN_OR_RETURN_UNWRAP(&orig, args[0].As<Object>());
70
5
    md = EVP_MD_CTX_md(orig->mdctx_.get());
71
  } else {
72
11012
    const Utf8Value hash_type(env->isolate(), args[0]);
73
5506
    md = EVP_get_digestbyname(*hash_type);
74
  }
75
76
5511
  Maybe<unsigned int> xof_md_len = Nothing<unsigned int>();
77
16533
  if (!args[1]->IsUndefined()) {
78
26
    CHECK(args[1]->IsUint32());
79
39
    xof_md_len = Just<unsigned int>(args[1].As<Uint32>()->Value());
80
  }
81
82
5511
  Hash* hash = new Hash(env, args.This());
83

5511
  if (md == nullptr || !hash->HashInit(md, xof_md_len)) {
84
3
    return ThrowCryptoError(env, ERR_get_error(),
85
3
                            "Digest method not supported");
86
  }
87
88

5513
  if (orig != nullptr &&
89
5
      0 >= EVP_MD_CTX_copy(hash->mdctx_.get(), orig->mdctx_.get())) {
90
    return ThrowCryptoError(env, ERR_get_error(), "Digest copy error");
91
  }
92
}
93
94
5509
bool Hash::HashInit(const EVP_MD* md, Maybe<unsigned int> xof_md_len) {
95
5509
  mdctx_.reset(EVP_MD_CTX_new());
96

5509
  if (!mdctx_ || EVP_DigestInit_ex(mdctx_.get(), md, nullptr) <= 0) {
97
    mdctx_.reset();
98
    return false;
99
  }
100
101
5509
  md_len_ = EVP_MD_size(md);
102

5522
  if (xof_md_len.IsJust() && xof_md_len.FromJust() != md_len_) {
103
    // This is a little hack to cause createHash to fail when an incorrect
104
    // hashSize option was passed for a non-XOF hash function.
105
12
    if ((EVP_MD_flags(md) & EVP_MD_FLAG_XOF) == 0) {
106
1
      EVPerr(EVP_F_EVP_DIGESTFINALXOF, EVP_R_NOT_XOF_OR_INVALID_LENGTH);
107
1
      return false;
108
    }
109
11
    md_len_ = xof_md_len.FromJust();
110
  }
111
112
5508
  return true;
113
}
114
115
5660
bool Hash::HashUpdate(const char* data, size_t len) {
116
5660
  if (!mdctx_)
117
    return false;
118
5660
  EVP_DigestUpdate(mdctx_.get(), data, len);
119
5660
  return true;
120
}
121
122
5660
void Hash::HashUpdate(const FunctionCallbackInfo<Value>& args) {
123
11320
  Decode<Hash>(args, [](Hash* hash, const FunctionCallbackInfo<Value>& args,
124
16980
                        const char* data, size_t size) {
125
5660
    Environment* env = Environment::GetCurrent(args);
126
5660
    if (UNLIKELY(size > INT_MAX))
127
      return THROW_ERR_OUT_OF_RANGE(env, "data is too long");
128
5660
    bool r = hash->HashUpdate(data, size);
129
16980
    args.GetReturnValue().Set(r);
130
16980
  });
131
5660
}
132
133
5493
void Hash::HashDigest(const FunctionCallbackInfo<Value>& args) {
134
5493
  Environment* env = Environment::GetCurrent(args);
135
136
  Hash* hash;
137
5493
  ASSIGN_OR_RETURN_UNWRAP(&hash, args.Holder());
138
139
5493
  enum encoding encoding = BUFFER;
140
5493
  if (args.Length() >= 1) {
141
5481
    encoding = ParseEncoding(env->isolate(), args[0], BUFFER);
142
  }
143
144
5493
  unsigned int len = hash->md_len_;
145
146
  // TODO(tniessen): SHA3_squeeze does not work for zero-length outputs on all
147
  // platforms and will cause a segmentation fault if called. This workaround
148
  // causes hash.digest() to correctly return an empty buffer / string.
149
  // See https://github.com/openssl/openssl/issues/9431.
150
151

5493
  if (!hash->digest_ && len > 0) {
152
    // Some hash algorithms such as SHA3 do not support calling
153
    // EVP_DigestFinal_ex more than once, however, Hash._flush
154
    // and Hash.digest can both be used to retrieve the digest,
155
    // so we need to cache it.
156
    // See https://github.com/nodejs/node/issues/28245.
157
158
5490
    char* md_value = MallocOpenSSL<char>(len);
159
10980
    ByteSource digest = ByteSource::Allocated(md_value, len);
160
161
5490
    size_t default_len = EVP_MD_CTX_size(hash->mdctx_.get());
162
    int ret;
163
5490
    if (len == default_len) {
164
5484
      ret = EVP_DigestFinal_ex(
165
5484
          hash->mdctx_.get(),
166
          reinterpret_cast<unsigned char*>(md_value),
167
5484
          &len);
168
      // The output length should always equal hash->md_len_
169
5484
      CHECK_EQ(len, hash->md_len_);
170
    } else {
171
12
      ret = EVP_DigestFinalXOF(
172
6
          hash->mdctx_.get(),
173
          reinterpret_cast<unsigned char*>(md_value),
174
6
          len);
175
    }
176
177
5490
    if (ret != 1)
178
      return ThrowCryptoError(env, ERR_get_error());
179
180
5490
    hash->digest_ = std::move(digest);
181
  }
182
183
  Local<Value> error;
184
  MaybeLocal<Value> rc =
185
      StringBytes::Encode(env->isolate(),
186
5493
                          hash->digest_.get(),
187
                          len,
188
                          encoding,
189
10986
                          &error);
190
5493
  if (rc.IsEmpty()) {
191
    CHECK(!error.IsEmpty());
192
    env->isolate()->ThrowException(error);
193
    return;
194
  }
195
10986
  args.GetReturnValue().Set(rc.FromMaybe(Local<Value>()));
196
}
197
198
73
HashConfig::HashConfig(HashConfig&& other) noexcept
199
73
    : mode(other.mode),
200
73
      in(std::move(other.in)),
201
73
      digest(other.digest),
202
292
      length(other.length) {}
203
204
HashConfig& HashConfig::operator=(HashConfig&& other) noexcept {
205
  if (&other == this) return *this;
206
  this->~HashConfig();
207
  return *new (this) HashConfig(std::move(other));
208
}
209
210
void HashConfig::MemoryInfo(MemoryTracker* tracker) const {
211
  // If the Job is sync, then the HashConfig does not own the data.
212
  if (mode == kCryptoJobAsync)
213
    tracker->TrackFieldWithSize("in", in.size());
214
}
215
216
73
Maybe<bool> HashTraits::EncodeOutput(
217
    Environment* env,
218
    const HashConfig& params,
219
    ByteSource* out,
220
    v8::Local<v8::Value>* result) {
221
146
  *result = out->ToArrayBuffer(env);
222
73
  return Just(!result->IsEmpty());
223
}
224
225
74
Maybe<bool> HashTraits::AdditionalConfig(
226
    CryptoJobMode mode,
227
    const FunctionCallbackInfo<Value>& args,
228
    unsigned int offset,
229
    HashConfig* params) {
230
74
  Environment* env = Environment::GetCurrent(args);
231
232
74
  params->mode = mode;
233
234
296
  CHECK(args[offset]->IsString());  // Hash algorithm
235
222
  Utf8Value digest(env->isolate(), args[offset]);
236
74
  params->digest = EVP_get_digestbyname(*digest);
237
74
  if (UNLIKELY(params->digest == nullptr)) {
238
    char msg[1024];
239
    snprintf(msg, sizeof(msg), "Invalid digest: %s", *digest);
240
    THROW_ERR_CRYPTO_INVALID_DIGEST(env);
241
    return Nothing<bool>();
242
  }
243
244
222
  ArrayBufferOrViewContents<char> data(args[offset + 1]);
245
74
  if (UNLIKELY(!data.CheckSizeInt32())) {
246
    THROW_ERR_OUT_OF_RANGE(env, "data is too big");
247
    return Nothing<bool>();
248
  }
249
  params->in = mode == kCryptoJobAsync
250
148
      ? data.ToCopy()
251
74
      : data.ToByteSource();
252
253
74
  unsigned int expected = EVP_MD_size(params->digest);
254
74
  params->length = expected;
255
222
  if (UNLIKELY(args[offset + 2]->IsUint32())) {
256
    // length is expressed in terms of bits
257
5
    params->length =
258
15
        static_cast<uint32_t>(args[offset + 2]
259
15
            .As<Uint32>()->Value()) / CHAR_BIT;
260
5
    if (params->length != expected) {
261
1
      if ((EVP_MD_flags(params->digest) & EVP_MD_FLAG_XOF) == 0) {
262
1
        THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Digest method not supported");
263
1
        return Nothing<bool>();
264
      }
265
    }
266
  }
267
268
73
  return Just(true);
269
}
270
271
73
bool HashTraits::DeriveBits(
272
    Environment* env,
273
    const HashConfig& params,
274
    ByteSource* out) {
275
146
  EVPMDPointer ctx(EVP_MD_CTX_new());
276
277


73
  if (UNLIKELY(!ctx ||
278
               EVP_DigestInit_ex(ctx.get(), params.digest, nullptr) <= 0 ||
279
               EVP_DigestUpdate(
280
                   ctx.get(),
281
                   params.in.get(),
282
                   params.in.size()) <= 0)) {
283
    return false;
284
  }
285
286
73
  if (LIKELY(params.length > 0)) {
287
73
    unsigned int length = params.length;
288
73
    char* data = MallocOpenSSL<char>(length);
289
146
    ByteSource buf = ByteSource::Allocated(data, length);
290
73
    unsigned char* ptr = reinterpret_cast<unsigned char*>(data);
291
292
73
    size_t expected = EVP_MD_CTX_size(ctx.get());
293
294
73
    int ret = (length == expected)
295
73
        ? EVP_DigestFinal_ex(ctx.get(), ptr, &length)
296
73
        : EVP_DigestFinalXOF(ctx.get(), ptr, length);
297
298
73
    if (UNLIKELY(ret != 1))
299
      return false;
300
301
73
    *out = std::move(buf);
302
  }
303
304
73
  return true;
305
}
306
307
}  // namespace crypto
308

14073
}  // namespace node