GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_random.cc Lines: 102 118 86.4 %
Date: 2022-04-23 04:14:58 Branches: 49 86 57.0 %

Line Branch Exec Source
1
#include "crypto/crypto_random.h"
2
#include "crypto/crypto_util.h"
3
#include "allocated_buffer-inl.h"
4
#include "async_wrap-inl.h"
5
#include "env-inl.h"
6
#include "memory_tracker-inl.h"
7
#include "threadpoolwork-inl.h"
8
#include "v8.h"
9
10
#include <openssl/bn.h>
11
#include <openssl/rand.h>
12
13
namespace node {
14
15
using v8::ArrayBuffer;
16
using v8::BackingStore;
17
using v8::False;
18
using v8::FunctionCallbackInfo;
19
using v8::Just;
20
using v8::Local;
21
using v8::Maybe;
22
using v8::Nothing;
23
using v8::Object;
24
using v8::True;
25
using v8::Uint32;
26
using v8::Value;
27
28
namespace crypto {
29
428
Maybe<bool> RandomBytesTraits::EncodeOutput(
30
    Environment* env,
31
    const RandomBytesConfig& params,
32
    ByteSource* unused,
33
    v8::Local<v8::Value>* result) {
34
856
  *result = v8::Undefined(env->isolate());
35
428
  return Just(!result->IsEmpty());
36
}
37
38
428
Maybe<bool> RandomBytesTraits::AdditionalConfig(
39
    CryptoJobMode mode,
40
    const FunctionCallbackInfo<Value>& args,
41
    unsigned int offset,
42
    RandomBytesConfig* params) {
43
428
  Environment* env = Environment::GetCurrent(args);
44

856
  CHECK(IsAnyByteSource(args[offset]));  // Buffer to fill
45

856
  CHECK(args[offset + 1]->IsUint32());  // Offset
46

856
  CHECK(args[offset + 2]->IsUint32());  // Size
47
48
1284
  ArrayBufferOrViewContents<unsigned char> in(args[offset]);
49
50
1284
  const uint32_t byte_offset = args[offset + 1].As<Uint32>()->Value();
51
1284
  const uint32_t size = args[offset + 2].As<Uint32>()->Value();
52
428
  CHECK_GE(byte_offset + size, byte_offset);  // Overflow check.
53
428
  CHECK_LE(byte_offset + size, in.size());  // Bounds check.
54
55
428
  if (UNLIKELY(size > INT_MAX)) {
56
    THROW_ERR_OUT_OF_RANGE(env, "buffer is too large");
57
    return Nothing<bool>();
58
  }
59
60
428
  params->buffer = in.data() + byte_offset;
61
428
  params->size = size;
62
63
428
  return Just(true);
64
}
65
66
428
bool RandomBytesTraits::DeriveBits(
67
    Environment* env,
68
    const RandomBytesConfig& params,
69
    ByteSource* unused) {
70
428
  CheckEntropy();  // Ensure that OpenSSL's PRNG is properly seeded.
71
428
  return RAND_bytes(params.buffer, params.size) != 0;
72
}
73
74
void RandomPrimeConfig::MemoryInfo(MemoryTracker* tracker) const {
75
  tracker->TrackFieldWithSize("prime", prime ? bits * 8 : 0);
76
}
77
78
15
Maybe<bool> RandomPrimeTraits::EncodeOutput(
79
    Environment* env,
80
    const RandomPrimeConfig& params,
81
    ByteSource* unused,
82
    v8::Local<v8::Value>* result) {
83
15
  size_t size = BN_num_bytes(params.prime.get());
84
  std::shared_ptr<BackingStore> store =
85
15
      ArrayBuffer::NewBackingStore(env->isolate(), size);
86
15
  BN_bn2binpad(
87
15
      params.prime.get(),
88
15
      reinterpret_cast<unsigned char*>(store->Data()),
89
      size);
90
30
  *result = ArrayBuffer::New(env->isolate(), store);
91
15
  return Just(true);
92
}
93
94
21
Maybe<bool> RandomPrimeTraits::AdditionalConfig(
95
    CryptoJobMode mode,
96
    const FunctionCallbackInfo<Value>& args,
97
    unsigned int offset,
98
    RandomPrimeConfig* params) {
99
21
  ClearErrorOnReturn clear_error;
100
21
  Environment* env = Environment::GetCurrent(args);
101

42
  CHECK(args[offset]->IsUint32());  // Size
102

42
  CHECK(args[offset + 1]->IsBoolean());  // Safe
103
104
63
  const uint32_t size = args[offset].As<Uint32>()->Value();
105
42
  bool safe = args[offset + 1]->IsTrue();
106
107

63
  if (!args[offset + 2]->IsUndefined()) {
108
24
    ArrayBufferOrViewContents<unsigned char> add(args[offset + 2]);
109
12
    params->add.reset(BN_bin2bn(add.data(), add.size(), nullptr));
110
12
    if (!params->add) {
111
      THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime");
112
      return Nothing<bool>();
113
    }
114
  }
115
116

63
  if (!args[offset + 3]->IsUndefined()) {
117
16
    ArrayBufferOrViewContents<unsigned char> rem(args[offset + 3]);
118
8
    params->rem.reset(BN_bin2bn(rem.data(), rem.size(), nullptr));
119
8
    if (!params->rem) {
120
      THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime");
121
      return Nothing<bool>();
122
    }
123
  }
124
125
  // The JS interface already ensures that the (positive) size fits into an int.
126
21
  int bits = static_cast<int>(size);
127
21
  CHECK_GT(bits, 0);
128
129
21
  if (params->add) {
130
12
    if (BN_num_bits(params->add.get()) > bits) {
131
      // If we allowed this, the best case would be returning a static prime
132
      // that wasn't generated randomly. The worst case would be an infinite
133
      // loop within OpenSSL, blocking the main thread or one of the threads
134
      // in the thread pool.
135
3
      THROW_ERR_OUT_OF_RANGE(env, "invalid options.add");
136
3
      return Nothing<bool>();
137
    }
138
139
9
    if (params->rem) {
140
7
      if (BN_cmp(params->add.get(), params->rem.get()) != 1) {
141
        // This would definitely lead to an infinite loop if allowed since
142
        // OpenSSL does not check this condition.
143
3
        THROW_ERR_OUT_OF_RANGE(env, "invalid options.rem");
144
3
        return Nothing<bool>();
145
      }
146
    }
147
  }
148
149
15
  params->bits = bits;
150
15
  params->safe = safe;
151
15
  params->prime.reset(BN_secure_new());
152
15
  if (!params->prime) {
153
    THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime");
154
    return Nothing<bool>();
155
  }
156
157
15
  return Just(true);
158
}
159
160
15
bool RandomPrimeTraits::DeriveBits(
161
    Environment* env,
162
    const RandomPrimeConfig& params,
163
    ByteSource* unused) {
164
165
15
  CheckEntropy();
166
167
30
  if (BN_generate_prime_ex(
168
          params.prime.get(),
169
15
          params.bits,
170
15
          params.safe ? 1 : 0,
171
15
          params.add.get(),
172
15
          params.rem.get(),
173
15
          nullptr) == 0) {
174
    return false;
175
  }
176
177
15
  return true;
178
}
179
180
void CheckPrimeConfig::MemoryInfo(MemoryTracker* tracker) const {
181
  tracker->TrackFieldWithSize(
182
      "prime", candidate ? BN_num_bytes(candidate.get()) : 0);
183
}
184
185
24
Maybe<bool> CheckPrimeTraits::AdditionalConfig(
186
    CryptoJobMode mode,
187
    const FunctionCallbackInfo<Value>& args,
188
    unsigned int offset,
189
    CheckPrimeConfig* params) {
190
24
  Environment* env = Environment::GetCurrent(args);
191
192
72
  ArrayBufferOrViewContents<unsigned char> candidate(args[offset]);
193
194
  params->candidate =
195
48
      BignumPointer(BN_bin2bn(
196
24
          candidate.data(),
197
24
          candidate.size(),
198
24
          nullptr));
199
200

48
  CHECK(args[offset + 1]->IsUint32());  // Checks
201
202
72
  const int checks = static_cast<int>(args[offset + 1].As<Uint32>()->Value());
203
24
  if (checks < 0) {
204
    THROW_ERR_OUT_OF_RANGE(env, "invalid options.checks");
205
    return Nothing<bool>();
206
  }
207
208
24
  params->checks = checks;
209
210
24
  return Just(true);
211
}
212
213
24
bool CheckPrimeTraits::DeriveBits(
214
    Environment* env,
215
    const CheckPrimeConfig& params,
216
    ByteSource* out) {
217
218
48
  BignumCtxPointer ctx(BN_CTX_new());
219
220
48
  int ret = BN_is_prime_ex(
221
24
            params.candidate.get(),
222
24
            params.checks,
223
            ctx.get(),
224
            nullptr);
225
24
  if (ret < 0) return false;
226
24
  char* data = MallocOpenSSL<char>(1);
227
24
  data[0] = ret;
228
24
  *out = ByteSource::Allocated(data, 1);
229
24
  return true;
230
}
231
232
24
Maybe<bool> CheckPrimeTraits::EncodeOutput(
233
    Environment* env,
234
    const CheckPrimeConfig& params,
235
    ByteSource* out,
236
    v8::Local<v8::Value>* result) {
237
24
  *result = out->get()[0] ? True(env->isolate()) : False(env->isolate());
238
24
  return Just(true);
239
}
240
241
namespace Random {
242
627
void Initialize(Environment* env, Local<Object> target) {
243
627
  RandomBytesJob::Initialize(env, target);
244
627
  RandomPrimeJob::Initialize(env, target);
245
627
  CheckPrimeJob::Initialize(env, target);
246
627
}
247
248
5013
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
249
5013
  RandomBytesJob::RegisterExternalReferences(registry);
250
5013
  RandomPrimeJob::RegisterExternalReferences(registry);
251
5013
  CheckPrimeJob::RegisterExternalReferences(registry);
252
5013
}
253
}  // namespace Random
254
}  // namespace crypto
255
}  // namespace node