• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::Boolean;
17 using v8::FunctionCallbackInfo;
18 using v8::Int32;
19 using v8::Just;
20 using v8::Local;
21 using v8::Maybe;
22 using v8::Nothing;
23 using v8::Object;
24 using v8::Uint32;
25 using v8::Value;
26 
27 namespace crypto {
EncodeOutput(Environment * env,const RandomBytesConfig & params,ByteSource * unused,v8::Local<v8::Value> * result)28 Maybe<bool> RandomBytesTraits::EncodeOutput(
29     Environment* env,
30     const RandomBytesConfig& params,
31     ByteSource* unused,
32     v8::Local<v8::Value>* result) {
33   *result = v8::Undefined(env->isolate());
34   return Just(!result->IsEmpty());
35 }
36 
AdditionalConfig(CryptoJobMode mode,const FunctionCallbackInfo<Value> & args,unsigned int offset,RandomBytesConfig * params)37 Maybe<bool> RandomBytesTraits::AdditionalConfig(
38     CryptoJobMode mode,
39     const FunctionCallbackInfo<Value>& args,
40     unsigned int offset,
41     RandomBytesConfig* params) {
42   CHECK(IsAnyByteSource(args[offset]));  // Buffer to fill
43   CHECK(args[offset + 1]->IsUint32());  // Offset
44   CHECK(args[offset + 2]->IsUint32());  // Size
45 
46   ArrayBufferOrViewContents<unsigned char> in(args[offset]);
47 
48   const uint32_t byte_offset = args[offset + 1].As<Uint32>()->Value();
49   const uint32_t size = args[offset + 2].As<Uint32>()->Value();
50   CHECK_GE(byte_offset + size, byte_offset);  // Overflow check.
51   CHECK_LE(byte_offset + size, in.size());  // Bounds check.
52 
53   params->buffer = in.data() + byte_offset;
54   params->size = size;
55 
56   return Just(true);
57 }
58 
DeriveBits(Environment * env,const RandomBytesConfig & params,ByteSource * unused)59 bool RandomBytesTraits::DeriveBits(
60     Environment* env,
61     const RandomBytesConfig& params,
62     ByteSource* unused) {
63   return CSPRNG(params.buffer, params.size).is_ok();
64 }
65 
MemoryInfo(MemoryTracker * tracker) const66 void RandomPrimeConfig::MemoryInfo(MemoryTracker* tracker) const {
67   tracker->TrackFieldWithSize("prime", prime ? bits * 8 : 0);
68 }
69 
EncodeOutput(Environment * env,const RandomPrimeConfig & params,ByteSource * unused,v8::Local<v8::Value> * result)70 Maybe<bool> RandomPrimeTraits::EncodeOutput(
71     Environment* env,
72     const RandomPrimeConfig& params,
73     ByteSource* unused,
74     v8::Local<v8::Value>* result) {
75   size_t size = BN_num_bytes(params.prime.get());
76   std::shared_ptr<BackingStore> store =
77       ArrayBuffer::NewBackingStore(env->isolate(), size);
78   BN_bn2binpad(
79       params.prime.get(),
80       reinterpret_cast<unsigned char*>(store->Data()),
81       size);
82   *result = ArrayBuffer::New(env->isolate(), store);
83   return Just(true);
84 }
85 
AdditionalConfig(CryptoJobMode mode,const FunctionCallbackInfo<Value> & args,unsigned int offset,RandomPrimeConfig * params)86 Maybe<bool> RandomPrimeTraits::AdditionalConfig(
87     CryptoJobMode mode,
88     const FunctionCallbackInfo<Value>& args,
89     unsigned int offset,
90     RandomPrimeConfig* params) {
91   ClearErrorOnReturn clear_error;
92   Environment* env = Environment::GetCurrent(args);
93   CHECK(args[offset]->IsUint32());  // Size
94   CHECK(args[offset + 1]->IsBoolean());  // Safe
95 
96   const uint32_t size = args[offset].As<Uint32>()->Value();
97   bool safe = args[offset + 1]->IsTrue();
98 
99   if (!args[offset + 2]->IsUndefined()) {
100     ArrayBufferOrViewContents<unsigned char> add(args[offset + 2]);
101     params->add.reset(BN_bin2bn(add.data(), add.size(), nullptr));
102     if (!params->add) {
103       THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime");
104       return Nothing<bool>();
105     }
106   }
107 
108   if (!args[offset + 3]->IsUndefined()) {
109     ArrayBufferOrViewContents<unsigned char> rem(args[offset + 3]);
110     params->rem.reset(BN_bin2bn(rem.data(), rem.size(), nullptr));
111     if (!params->rem) {
112       THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime");
113       return Nothing<bool>();
114     }
115   }
116 
117   // The JS interface already ensures that the (positive) size fits into an int.
118   int bits = static_cast<int>(size);
119   CHECK_GT(bits, 0);
120 
121   if (params->add) {
122     if (BN_num_bits(params->add.get()) > bits) {
123       // If we allowed this, the best case would be returning a static prime
124       // that wasn't generated randomly. The worst case would be an infinite
125       // loop within OpenSSL, blocking the main thread or one of the threads
126       // in the thread pool.
127       THROW_ERR_OUT_OF_RANGE(env, "invalid options.add");
128       return Nothing<bool>();
129     }
130 
131     if (params->rem) {
132       if (BN_cmp(params->add.get(), params->rem.get()) != 1) {
133         // This would definitely lead to an infinite loop if allowed since
134         // OpenSSL does not check this condition.
135         THROW_ERR_OUT_OF_RANGE(env, "invalid options.rem");
136         return Nothing<bool>();
137       }
138     }
139   }
140 
141   params->bits = bits;
142   params->safe = safe;
143   params->prime.reset(BN_secure_new());
144   if (!params->prime) {
145     THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime");
146     return Nothing<bool>();
147   }
148 
149   return Just(true);
150 }
151 
DeriveBits(Environment * env,const RandomPrimeConfig & params,ByteSource * unused)152 bool RandomPrimeTraits::DeriveBits(Environment* env,
153                                    const RandomPrimeConfig& params,
154                                    ByteSource* unused) {
155   // BN_generate_prime_ex() calls RAND_bytes_ex() internally.
156   // Make sure the CSPRNG is properly seeded.
157   CHECK(CSPRNG(nullptr, 0).is_ok());
158 
159   if (BN_generate_prime_ex(
160           params.prime.get(),
161           params.bits,
162           params.safe ? 1 : 0,
163           params.add.get(),
164           params.rem.get(),
165           nullptr) == 0) {
166     return false;
167   }
168 
169   return true;
170 }
171 
MemoryInfo(MemoryTracker * tracker) const172 void CheckPrimeConfig::MemoryInfo(MemoryTracker* tracker) const {
173   tracker->TrackFieldWithSize(
174       "prime", candidate ? BN_num_bytes(candidate.get()) : 0);
175 }
176 
AdditionalConfig(CryptoJobMode mode,const FunctionCallbackInfo<Value> & args,unsigned int offset,CheckPrimeConfig * params)177 Maybe<bool> CheckPrimeTraits::AdditionalConfig(
178     CryptoJobMode mode,
179     const FunctionCallbackInfo<Value>& args,
180     unsigned int offset,
181     CheckPrimeConfig* params) {
182   ArrayBufferOrViewContents<unsigned char> candidate(args[offset]);
183 
184   params->candidate =
185       BignumPointer(BN_bin2bn(
186           candidate.data(),
187           candidate.size(),
188           nullptr));
189 
190   CHECK(args[offset + 1]->IsInt32());  // Checks
191   params->checks = args[offset + 1].As<Int32>()->Value();
192   CHECK_GE(params->checks, 0);
193 
194   return Just(true);
195 }
196 
DeriveBits(Environment * env,const CheckPrimeConfig & params,ByteSource * out)197 bool CheckPrimeTraits::DeriveBits(
198     Environment* env,
199     const CheckPrimeConfig& params,
200     ByteSource* out) {
201 
202   BignumCtxPointer ctx(BN_CTX_new());
203 
204   int ret = BN_is_prime_ex(
205             params.candidate.get(),
206             params.checks,
207             ctx.get(),
208             nullptr);
209   if (ret < 0) return false;
210   ByteSource::Builder buf(1);
211   buf.data<char>()[0] = ret;
212   *out = std::move(buf).release();
213   return true;
214 }
215 
EncodeOutput(Environment * env,const CheckPrimeConfig & params,ByteSource * out,v8::Local<v8::Value> * result)216 Maybe<bool> CheckPrimeTraits::EncodeOutput(
217     Environment* env,
218     const CheckPrimeConfig& params,
219     ByteSource* out,
220     v8::Local<v8::Value>* result) {
221   *result = Boolean::New(env->isolate(), out->data<char>()[0] != 0);
222   return Just(true);
223 }
224 
225 namespace Random {
Initialize(Environment * env,Local<Object> target)226 void Initialize(Environment* env, Local<Object> target) {
227   RandomBytesJob::Initialize(env, target);
228   RandomPrimeJob::Initialize(env, target);
229   CheckPrimeJob::Initialize(env, target);
230 }
231 
RegisterExternalReferences(ExternalReferenceRegistry * registry)232 void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
233   RandomBytesJob::RegisterExternalReferences(registry);
234   RandomPrimeJob::RegisterExternalReferences(registry);
235   CheckPrimeJob::RegisterExternalReferences(registry);
236 }
237 }  // namespace Random
238 }  // namespace crypto
239 }  // namespace node
240