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: 74 159 46.5 %
Date: 2021-02-19 04:08:54 Branches: 26 106 24.5 %

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
14216
Hash::Hash(Environment* env, Local<Object> wrap) : BaseObject(env, wrap) {
28
14216
  MakeWeak();
29
14216
}
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
2
void Hash::GetHashes(const FunctionCallbackInfo<Value>& args) {
37
2
  Environment* env = Environment::GetCurrent(args);
38
4
  CipherPushContext ctx(env);
39
2
  EVP_MD_do_all_sorted(array_push_back<EVP_MD>, &ctx);
40
6
  args.GetReturnValue().Set(ctx.ToJSArray());
41
2
}
42
43
2
void Hash::Initialize(Environment* env, Local<Object> target) {
44
2
  Local<FunctionTemplate> t = env->NewFunctionTemplate(New);
45
46
6
  t->InstanceTemplate()->SetInternalFieldCount(
47
2
      Hash::kInternalFieldCount);
48
4
  t->Inherit(BaseObject::GetConstructorTemplate(env));
49
50
2
  env->SetProtoMethod(t, "update", HashUpdate);
51
2
  env->SetProtoMethod(t, "digest", HashDigest);
52
53
2
  env->SetConstructorFunction(target, "Hash", t);
54
55
2
  env->SetMethodNoSideEffect(target, "getHashes", GetHashes);
56
57
2
  HashJob::Initialize(env, target);
58
2
}
59
60
14216
void Hash::New(const FunctionCallbackInfo<Value>& args) {
61
14216
  Environment* env = Environment::GetCurrent(args);
62
63
14216
  const Hash* orig = nullptr;
64
14216
  const EVP_MD* md = nullptr;
65
66
28432
  if (args[0]->IsObject()) {
67
    ASSIGN_OR_RETURN_UNWRAP(&orig, args[0].As<Object>());
68
    md = EVP_MD_CTX_md(orig->mdctx_.get());
69
  } else {
70
28432
    const Utf8Value hash_type(env->isolate(), args[0]);
71
14216
    md = EVP_get_digestbyname(*hash_type);
72
  }
73
74
14216
  Maybe<unsigned int> xof_md_len = Nothing<unsigned int>();
75
42648
  if (!args[1]->IsUndefined()) {
76
    CHECK(args[1]->IsUint32());
77
    xof_md_len = Just<unsigned int>(args[1].As<Uint32>()->Value());
78
  }
79
80
14216
  Hash* hash = new Hash(env, args.This());
81

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

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

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

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

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

366
}  // namespace node