File: | out/../src/crypto/crypto_hash.cc |
Warning: | line 101, column 12 Potential leak of memory pointed to by 'hash' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | #include "crypto/crypto_hash.h" | |||
2 | #include "async_wrap-inl.h" | |||
3 | #include "base_object-inl.h" | |||
4 | #include "env-inl.h" | |||
5 | #include "memory_tracker-inl.h" | |||
6 | #include "string_bytes.h" | |||
7 | #include "threadpoolwork-inl.h" | |||
8 | #include "v8.h" | |||
9 | ||||
10 | #include <cstdio> | |||
11 | ||||
12 | namespace node { | |||
13 | ||||
14 | using v8::FunctionCallbackInfo; | |||
15 | using v8::FunctionTemplate; | |||
16 | using v8::Just; | |||
17 | using v8::Local; | |||
18 | using v8::Maybe; | |||
19 | using v8::MaybeLocal; | |||
20 | using v8::Nothing; | |||
21 | using v8::Object; | |||
22 | using v8::Uint32; | |||
23 | using v8::Value; | |||
24 | ||||
25 | namespace crypto { | |||
26 | Hash::Hash(Environment* env, Local<Object> wrap) : BaseObject(env, wrap) { | |||
27 | MakeWeak(); | |||
28 | } | |||
29 | ||||
30 | void Hash::MemoryInfo(MemoryTracker* tracker) const { | |||
31 | tracker->TrackFieldWithSize("mdctx", mdctx_ ? kSizeOf_EVP_MD_CTX : 0); | |||
32 | tracker->TrackFieldWithSize("md", digest_ ? md_len_ : 0); | |||
33 | } | |||
34 | ||||
35 | void Hash::GetHashes(const FunctionCallbackInfo<Value>& args) { | |||
36 | Environment* env = Environment::GetCurrent(args); | |||
37 | MarkPopErrorOnReturn mark_pop_error_on_return; | |||
38 | CipherPushContext ctx(env); | |||
39 | EVP_MD_do_all_sorted( | |||
40 | #if OPENSSL_VERSION_MAJOR3 >= 3 | |||
41 | array_push_back<EVP_MD, | |||
42 | EVP_MD_fetch, | |||
43 | EVP_MD_free, | |||
44 | EVP_get_digestbyname, | |||
45 | EVP_MD_get0_name>, | |||
46 | #else | |||
47 | array_push_back<EVP_MD>, | |||
48 | #endif | |||
49 | &ctx); | |||
50 | args.GetReturnValue().Set(ctx.ToJSArray()); | |||
51 | } | |||
52 | ||||
53 | void Hash::Initialize(Environment* env, Local<Object> target) { | |||
54 | Local<FunctionTemplate> t = env->NewFunctionTemplate(New); | |||
55 | ||||
56 | t->InstanceTemplate()->SetInternalFieldCount( | |||
57 | Hash::kInternalFieldCount); | |||
58 | t->Inherit(BaseObject::GetConstructorTemplate(env)); | |||
59 | ||||
60 | env->SetProtoMethod(t, "update", HashUpdate); | |||
61 | env->SetProtoMethod(t, "digest", HashDigest); | |||
62 | ||||
63 | env->SetConstructorFunction(target, "Hash", t); | |||
64 | ||||
65 | env->SetMethodNoSideEffect(target, "getHashes", GetHashes); | |||
66 | ||||
67 | HashJob::Initialize(env, target); | |||
68 | } | |||
69 | ||||
70 | void Hash::RegisterExternalReferences(ExternalReferenceRegistry* registry) { | |||
71 | registry->Register(New); | |||
72 | registry->Register(HashUpdate); | |||
73 | registry->Register(HashDigest); | |||
74 | registry->Register(GetHashes); | |||
75 | ||||
76 | HashJob::RegisterExternalReferences(registry); | |||
77 | } | |||
78 | ||||
79 | void Hash::New(const FunctionCallbackInfo<Value>& args) { | |||
80 | Environment* env = Environment::GetCurrent(args); | |||
81 | ||||
82 | const Hash* orig = nullptr; | |||
83 | const EVP_MD* md = nullptr; | |||
84 | ||||
85 | if (args[0]->IsObject()) { | |||
| ||||
86 | ASSIGN_OR_RETURN_UNWRAP(&orig, args[0].As<Object>())do { *&orig = static_cast<typename std::remove_reference <decltype(*&orig)>::type>( BaseObject::FromJSObject (args[0].As<Object>())); if (*&orig == nullptr) return ; } while (0); | |||
87 | md = EVP_MD_CTX_md(orig->mdctx_.get()); | |||
88 | } else { | |||
89 | const Utf8Value hash_type(env->isolate(), args[0]); | |||
90 | md = EVP_get_digestbyname(*hash_type); | |||
91 | } | |||
92 | ||||
93 | Maybe<unsigned int> xof_md_len = Nothing<unsigned int>(); | |||
94 | if (!args[1]->IsUndefined()) { | |||
95 | CHECK(args[1]->IsUint32())do { if (__builtin_expect(!!(!(args[1]->IsUint32())), 0)) { do { static const node::AssertionInfo args = { "../src/crypto/crypto_hash.cc" ":" "95", "args[1]->IsUint32()", __PRETTY_FUNCTION__ }; node ::Assert(args); } while (0); } } while (0); | |||
96 | xof_md_len = Just<unsigned int>(args[1].As<Uint32>()->Value()); | |||
97 | } | |||
98 | ||||
99 | Hash* hash = new Hash(env, args.This()); | |||
100 | if (md == nullptr || !hash->HashInit(md, xof_md_len)) { | |||
101 | return ThrowCryptoError(env, ERR_get_error(), | |||
| ||||
102 | "Digest method not supported"); | |||
103 | } | |||
104 | ||||
105 | if (orig != nullptr && | |||
106 | 0 >= EVP_MD_CTX_copy(hash->mdctx_.get(), orig->mdctx_.get())) { | |||
107 | return ThrowCryptoError(env, ERR_get_error(), "Digest copy error"); | |||
108 | } | |||
109 | } | |||
110 | ||||
111 | bool Hash::HashInit(const EVP_MD* md, Maybe<unsigned int> xof_md_len) { | |||
112 | mdctx_.reset(EVP_MD_CTX_new()); | |||
113 | if (!mdctx_ || EVP_DigestInit_ex(mdctx_.get(), md, nullptr) <= 0) { | |||
114 | mdctx_.reset(); | |||
115 | return false; | |||
116 | } | |||
117 | ||||
118 | md_len_ = EVP_MD_sizeEVP_MD_get_size(md); | |||
119 | if (xof_md_len.IsJust() && xof_md_len.FromJust() != md_len_) { | |||
120 | // This is a little hack to cause createHash to fail when an incorrect | |||
121 | // hashSize option was passed for a non-XOF hash function. | |||
122 | if ((EVP_MD_flagsEVP_MD_get_flags(md) & EVP_MD_FLAG_XOF0x0002) == 0) { | |||
123 | EVPerr(EVP_F_EVP_DIGESTFINALXOF, EVP_R_NOT_XOF_OR_INVALID_LENGTH)(ERR_new(), ERR_set_debug("../src/crypto/crypto_hash.cc",123, "(unknown function)"), ERR_set_error)(6, (178), __null); | |||
124 | return false; | |||
125 | } | |||
126 | md_len_ = xof_md_len.FromJust(); | |||
127 | } | |||
128 | ||||
129 | return true; | |||
130 | } | |||
131 | ||||
132 | bool Hash::HashUpdate(const char* data, size_t len) { | |||
133 | if (!mdctx_) | |||
134 | return false; | |||
135 | return EVP_DigestUpdate(mdctx_.get(), data, len) == 1; | |||
136 | } | |||
137 | ||||
138 | void Hash::HashUpdate(const FunctionCallbackInfo<Value>& args) { | |||
139 | Decode<Hash>(args, [](Hash* hash, const FunctionCallbackInfo<Value>& args, | |||
140 | const char* data, size_t size) { | |||
141 | Environment* env = Environment::GetCurrent(args); | |||
142 | if (UNLIKELY(size > INT_MAX)__builtin_expect(!!(size > 2147483647), 0)) | |||
143 | return THROW_ERR_OUT_OF_RANGE(env, "data is too long"); | |||
144 | bool r = hash->HashUpdate(data, size); | |||
145 | args.GetReturnValue().Set(r); | |||
146 | }); | |||
147 | } | |||
148 | ||||
149 | void Hash::HashDigest(const FunctionCallbackInfo<Value>& args) { | |||
150 | Environment* env = Environment::GetCurrent(args); | |||
151 | ||||
152 | Hash* hash; | |||
153 | ASSIGN_OR_RETURN_UNWRAP(&hash, args.Holder())do { *&hash = static_cast<typename std::remove_reference <decltype(*&hash)>::type>( BaseObject::FromJSObject (args.Holder())); if (*&hash == nullptr) return ; } while (0); | |||
154 | ||||
155 | enum encoding encoding = BUFFER; | |||
156 | if (args.Length() >= 1) { | |||
157 | encoding = ParseEncoding(env->isolate(), args[0], BUFFER); | |||
158 | } | |||
159 | ||||
160 | unsigned int len = hash->md_len_; | |||
161 | ||||
162 | // TODO(tniessen): SHA3_squeeze does not work for zero-length outputs on all | |||
163 | // platforms and will cause a segmentation fault if called. This workaround | |||
164 | // causes hash.digest() to correctly return an empty buffer / string. | |||
165 | // See https://github.com/openssl/openssl/issues/9431. | |||
166 | ||||
167 | if (!hash->digest_ && len > 0) { | |||
168 | // Some hash algorithms such as SHA3 do not support calling | |||
169 | // EVP_DigestFinal_ex more than once, however, Hash._flush | |||
170 | // and Hash.digest can both be used to retrieve the digest, | |||
171 | // so we need to cache it. | |||
172 | // See https://github.com/nodejs/node/issues/28245. | |||
173 | ||||
174 | ByteSource::Builder digest(len); | |||
175 | ||||
176 | size_t default_len = EVP_MD_CTX_size(hash->mdctx_.get())EVP_MD_get_size(EVP_MD_CTX_get0_md(hash->mdctx_.get())); | |||
177 | int ret; | |||
178 | if (len == default_len) { | |||
179 | ret = EVP_DigestFinal_ex( | |||
180 | hash->mdctx_.get(), digest.data<unsigned char>(), &len); | |||
181 | // The output length should always equal hash->md_len_ | |||
182 | CHECK_EQ(len, hash->md_len_)do { if (__builtin_expect(!!(!((len) == (hash->md_len_))), 0)) { do { static const node::AssertionInfo args = { "../src/crypto/crypto_hash.cc" ":" "182", "(len) == (hash->md_len_)", __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } } while (0); | |||
183 | } else { | |||
184 | ret = EVP_DigestFinalXOF( | |||
185 | hash->mdctx_.get(), digest.data<unsigned char>(), len); | |||
186 | } | |||
187 | ||||
188 | if (ret != 1) | |||
189 | return ThrowCryptoError(env, ERR_get_error()); | |||
190 | ||||
191 | hash->digest_ = std::move(digest).release(); | |||
192 | } | |||
193 | ||||
194 | Local<Value> error; | |||
195 | MaybeLocal<Value> rc = StringBytes::Encode( | |||
196 | env->isolate(), hash->digest_.data<char>(), len, encoding, &error); | |||
197 | if (rc.IsEmpty()) { | |||
198 | CHECK(!error.IsEmpty())do { if (__builtin_expect(!!(!(!error.IsEmpty())), 0)) { do { static const node::AssertionInfo args = { "../src/crypto/crypto_hash.cc" ":" "198", "!error.IsEmpty()", __PRETTY_FUNCTION__ }; node:: Assert(args); } while (0); } } while (0); | |||
199 | env->isolate()->ThrowException(error); | |||
200 | return; | |||
201 | } | |||
202 | args.GetReturnValue().Set(rc.FromMaybe(Local<Value>())); | |||
203 | } | |||
204 | ||||
205 | HashConfig::HashConfig(HashConfig&& other) noexcept | |||
206 | : mode(other.mode), | |||
207 | in(std::move(other.in)), | |||
208 | digest(other.digest), | |||
209 | length(other.length) {} | |||
210 | ||||
211 | HashConfig& HashConfig::operator=(HashConfig&& other) noexcept { | |||
212 | if (&other == this) return *this; | |||
213 | this->~HashConfig(); | |||
214 | return *new (this) HashConfig(std::move(other)); | |||
215 | } | |||
216 | ||||
217 | void HashConfig::MemoryInfo(MemoryTracker* tracker) const { | |||
218 | // If the Job is sync, then the HashConfig does not own the data. | |||
219 | if (mode == kCryptoJobAsync) | |||
220 | tracker->TrackFieldWithSize("in", in.size()); | |||
221 | } | |||
222 | ||||
223 | Maybe<bool> HashTraits::EncodeOutput( | |||
224 | Environment* env, | |||
225 | const HashConfig& params, | |||
226 | ByteSource* out, | |||
227 | v8::Local<v8::Value>* result) { | |||
228 | *result = out->ToArrayBuffer(env); | |||
229 | return Just(!result->IsEmpty()); | |||
230 | } | |||
231 | ||||
232 | Maybe<bool> HashTraits::AdditionalConfig( | |||
233 | CryptoJobMode mode, | |||
234 | const FunctionCallbackInfo<Value>& args, | |||
235 | unsigned int offset, | |||
236 | HashConfig* params) { | |||
237 | Environment* env = Environment::GetCurrent(args); | |||
238 | ||||
239 | params->mode = mode; | |||
240 | ||||
241 | CHECK(args[offset]->IsString())do { if (__builtin_expect(!!(!(args[offset]->IsString())), 0)) { do { static const node::AssertionInfo args = { "../src/crypto/crypto_hash.cc" ":" "241", "args[offset]->IsString()", __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } } while (0); // Hash algorithm | |||
242 | Utf8Value digest(env->isolate(), args[offset]); | |||
243 | params->digest = EVP_get_digestbyname(*digest); | |||
244 | if (UNLIKELY(params->digest == nullptr)__builtin_expect(!!(params->digest == nullptr), 0)) { | |||
245 | THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); | |||
246 | return Nothing<bool>(); | |||
247 | } | |||
248 | ||||
249 | ArrayBufferOrViewContents<char> data(args[offset + 1]); | |||
250 | if (UNLIKELY(!data.CheckSizeInt32())__builtin_expect(!!(!data.CheckSizeInt32()), 0)) { | |||
251 | THROW_ERR_OUT_OF_RANGE(env, "data is too big"); | |||
252 | return Nothing<bool>(); | |||
253 | } | |||
254 | params->in = mode == kCryptoJobAsync | |||
255 | ? data.ToCopy() | |||
256 | : data.ToByteSource(); | |||
257 | ||||
258 | unsigned int expected = EVP_MD_sizeEVP_MD_get_size(params->digest); | |||
259 | params->length = expected; | |||
260 | if (UNLIKELY(args[offset + 2]->IsUint32())__builtin_expect(!!(args[offset + 2]->IsUint32()), 0)) { | |||
261 | // length is expressed in terms of bits | |||
262 | params->length = | |||
263 | static_cast<uint32_t>(args[offset + 2] | |||
264 | .As<Uint32>()->Value()) / CHAR_BIT8; | |||
265 | if (params->length != expected) { | |||
266 | if ((EVP_MD_flagsEVP_MD_get_flags(params->digest) & EVP_MD_FLAG_XOF0x0002) == 0) { | |||
267 | THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Digest method not supported"); | |||
268 | return Nothing<bool>(); | |||
269 | } | |||
270 | } | |||
271 | } | |||
272 | ||||
273 | return Just(true); | |||
274 | } | |||
275 | ||||
276 | bool HashTraits::DeriveBits( | |||
277 | Environment* env, | |||
278 | const HashConfig& params, | |||
279 | ByteSource* out) { | |||
280 | EVPMDPointer ctx(EVP_MD_CTX_new()); | |||
281 | ||||
282 | if (UNLIKELY(!ctx ||__builtin_expect(!!(!ctx || EVP_DigestInit_ex(ctx.get(), params .digest, nullptr) <= 0 || EVP_DigestUpdate( ctx.get(), params .in.data<char>(), params.in.size()) <= 0), 0) | |||
283 | EVP_DigestInit_ex(ctx.get(), params.digest, nullptr) <= 0 ||__builtin_expect(!!(!ctx || EVP_DigestInit_ex(ctx.get(), params .digest, nullptr) <= 0 || EVP_DigestUpdate( ctx.get(), params .in.data<char>(), params.in.size()) <= 0), 0) | |||
284 | EVP_DigestUpdate(__builtin_expect(!!(!ctx || EVP_DigestInit_ex(ctx.get(), params .digest, nullptr) <= 0 || EVP_DigestUpdate( ctx.get(), params .in.data<char>(), params.in.size()) <= 0), 0) | |||
285 | ctx.get(), params.in.data<char>(), params.in.size()) <= 0)__builtin_expect(!!(!ctx || EVP_DigestInit_ex(ctx.get(), params .digest, nullptr) <= 0 || EVP_DigestUpdate( ctx.get(), params .in.data<char>(), params.in.size()) <= 0), 0)) { | |||
286 | return false; | |||
287 | } | |||
288 | ||||
289 | if (LIKELY(params.length > 0)__builtin_expect(!!(params.length > 0), 1)) { | |||
290 | unsigned int length = params.length; | |||
291 | ByteSource::Builder buf(length); | |||
292 | ||||
293 | size_t expected = EVP_MD_CTX_size(ctx.get())EVP_MD_get_size(EVP_MD_CTX_get0_md(ctx.get())); | |||
294 | ||||
295 | int ret = | |||
296 | (length == expected) | |||
297 | ? EVP_DigestFinal_ex(ctx.get(), buf.data<unsigned char>(), &length) | |||
298 | : EVP_DigestFinalXOF(ctx.get(), buf.data<unsigned char>(), length); | |||
299 | ||||
300 | if (UNLIKELY(ret != 1)__builtin_expect(!!(ret != 1), 0)) | |||
301 | return false; | |||
302 | ||||
303 | *out = std::move(buf).release(); | |||
304 | } | |||
305 | ||||
306 | return true; | |||
307 | } | |||
308 | ||||
309 | } // namespace crypto | |||
310 | } // namespace node |