/src/node/src/crypto/crypto_timing.cc
Line  | Count  | Source  | 
1  |  | #include "crypto/crypto_timing.h"  | 
2  |  | #include "crypto/crypto_util.h"  | 
3  |  | #include "env-inl.h"  | 
4  |  | #include "node.h"  | 
5  |  | #include "node_debug.h"  | 
6  |  | #include "node_errors.h"  | 
7  |  | #include "v8.h"  | 
8  |  |  | 
9  |  | #include <openssl/crypto.h>  | 
10  |  |  | 
11  |  | namespace node { | 
12  |  |  | 
13  |  | using v8::CFunction;  | 
14  |  | using v8::FastApiCallbackOptions;  | 
15  |  | using v8::FunctionCallbackInfo;  | 
16  |  | using v8::HandleScope;  | 
17  |  | using v8::Local;  | 
18  |  | using v8::Object;  | 
19  |  | using v8::Value;  | 
20  |  |  | 
21  |  | namespace crypto { | 
22  |  | namespace Timing { | 
23  | 0  | void TimingSafeEqual(const FunctionCallbackInfo<Value>& args) { | 
24  |  |   // Moving the type checking into JS leads to test failures, most likely due  | 
25  |  |   // to V8 inlining certain parts of the wrapper. Therefore, keep them in C++.  | 
26  |  |   // Refs: https://github.com/nodejs/node/issues/34073.  | 
27  | 0  |   Environment* env = Environment::GetCurrent(args);  | 
28  | 0  |   if (!IsAnyBufferSource(args[0])) { | 
29  | 0  |     THROW_ERR_INVALID_ARG_TYPE(  | 
30  | 0  |       env, "The \"buf1\" argument must be an instance of "  | 
31  | 0  |       "ArrayBuffer, Buffer, TypedArray, or DataView.");  | 
32  | 0  |     return;  | 
33  | 0  |   }  | 
34  | 0  |   if (!IsAnyBufferSource(args[1])) { | 
35  | 0  |     THROW_ERR_INVALID_ARG_TYPE(  | 
36  | 0  |       env, "The \"buf2\" argument must be an instance of "  | 
37  | 0  |       "ArrayBuffer, Buffer, TypedArray, or DataView.");  | 
38  | 0  |     return;  | 
39  | 0  |   }  | 
40  |  |  | 
41  | 0  |   ArrayBufferOrViewContents<char> buf1(args[0]);  | 
42  | 0  |   ArrayBufferOrViewContents<char> buf2(args[1]);  | 
43  |  | 
  | 
44  | 0  |   if (buf1.size() != buf2.size()) { | 
45  | 0  |     THROW_ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH(env);  | 
46  | 0  |     return;  | 
47  | 0  |   }  | 
48  |  |  | 
49  | 0  |   return args.GetReturnValue().Set(  | 
50  | 0  |       CRYPTO_memcmp(buf1.data(), buf2.data(), buf1.size()) == 0);  | 
51  | 0  | }  | 
52  |  |  | 
53  |  | bool FastTimingSafeEqual(Local<Value> receiver,  | 
54  |  |                          Local<Value> a_obj,  | 
55  |  |                          Local<Value> b_obj,  | 
56  |  |                          // NOLINTNEXTLINE(runtime/references)  | 
57  | 0  |                          FastApiCallbackOptions& options) { | 
58  | 0  |   HandleScope scope(options.isolate);  | 
59  | 0  |   ArrayBufferViewContents<uint8_t> a(a_obj);  | 
60  | 0  |   ArrayBufferViewContents<uint8_t> b(b_obj);  | 
61  | 0  |   if (a.length() != b.length()) { | 
62  | 0  |     TRACK_V8_FAST_API_CALL("crypto.timingSafeEqual.error"); | 
63  | 0  |     THROW_ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH(options.isolate);  | 
64  | 0  |     return false;  | 
65  | 0  |   }  | 
66  |  |  | 
67  | 0  |   TRACK_V8_FAST_API_CALL("crypto.timingSafeEqual.ok"); | 
68  | 0  |   return CRYPTO_memcmp(a.data(), b.data(), a.length()) == 0;  | 
69  | 0  | }  | 
70  |  |  | 
71  |  | static CFunction fast_timing_safe_equal(CFunction::Make(FastTimingSafeEqual));  | 
72  |  |  | 
73  | 0  | void Initialize(Environment* env, Local<Object> target) { | 
74  | 0  |   SetFastMethodNoSideEffect(env->context(),  | 
75  | 0  |                             target,  | 
76  | 0  |                             "timingSafeEqual",  | 
77  | 0  |                             TimingSafeEqual,  | 
78  | 0  |                             &fast_timing_safe_equal);  | 
79  | 0  | }  | 
80  | 0  | void RegisterExternalReferences(ExternalReferenceRegistry* registry) { | 
81  | 0  |   registry->Register(TimingSafeEqual);  | 
82  | 0  |   registry->Register(fast_timing_safe_equal);  | 
83  | 0  | }  | 
84  |  | }  // namespace Timing  | 
85  |  |  | 
86  |  | }  // namespace crypto  | 
87  |  | }  // namespace node  |