| 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 |