GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_random.cc Lines: 102 118 86.4 %
Date: 2022-08-30 04:20:47 Branches: 49 86 57.0 %

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

870
  CHECK(IsAnyByteSource(args[offset]));  // Buffer to fill
44

870
  CHECK(args[offset + 1]->IsUint32());  // Offset
45

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

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

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

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

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

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