#include "crypto/crypto_pbkdf2.h" #include "crypto/crypto_util.h" #include "allocated_buffer-inl.h" #include "async_wrap-inl.h" #include "env-inl.h" #include "memory_tracker-inl.h" #include "node_buffer.h" #include "threadpoolwork-inl.h" #include "v8.h" namespace node { using v8::FunctionCallbackInfo; using v8::Int32; using v8::Just; using v8::Maybe; using v8::Nothing; using v8::Value; namespace crypto { PBKDF2Config::PBKDF2Config(PBKDF2Config&& other) noexcept : mode(other.mode), pass(std::move(other.pass)), salt(std::move(other.salt)), iterations(other.iterations), length(other.length), digest(other.digest) {} PBKDF2Config& PBKDF2Config::operator=(PBKDF2Config&& other) noexcept { if (&other == this) return *this; this->~PBKDF2Config(); return *new (this) PBKDF2Config(std::move(other)); } void PBKDF2Config::MemoryInfo(MemoryTracker* tracker) const { // The job is sync, the PBKDF2Config does not own the data. if (mode == kCryptoJobAsync) { tracker->TrackFieldWithSize("pass", pass.size()); tracker->TrackFieldWithSize("salt", salt.size()); } } Maybe PBKDF2Traits::EncodeOutput( Environment* env, const PBKDF2Config& params, ByteSource* out, v8::Local* result) { *result = out->ToArrayBuffer(env); return Just(!result->IsEmpty()); } // The input arguments for the job are: // 1. CryptoJobMode // 2. The password // 3. The salt // 4. The number of iterations // 5. The number of bytes to generate // 6. The digest algorithm name Maybe PBKDF2Traits::AdditionalConfig( CryptoJobMode mode, const FunctionCallbackInfo& args, unsigned int offset, PBKDF2Config* params) { Environment* env = Environment::GetCurrent(args); params->mode = mode; ArrayBufferOrViewContents pass(args[offset]); ArrayBufferOrViewContents salt(args[offset + 1]); if (UNLIKELY(!pass.CheckSizeInt32())) { THROW_ERR_OUT_OF_RANGE(env, "pass is too large"); return Nothing(); } if (UNLIKELY(!salt.CheckSizeInt32())) { THROW_ERR_OUT_OF_RANGE(env, "salt is too large"); return Nothing(); } params->pass = mode == kCryptoJobAsync ? pass.ToCopy() : pass.ToByteSource(); params->salt = mode == kCryptoJobAsync ? salt.ToCopy() : salt.ToByteSource(); CHECK(args[offset + 2]->IsInt32()); // iteration_count CHECK(args[offset + 3]->IsInt32()); // length CHECK(args[offset + 4]->IsString()); // digest_name params->iterations = args[offset + 2].As()->Value(); if (params->iterations < 0) { THROW_ERR_OUT_OF_RANGE(env, "iterations must be <= %d", INT_MAX); return Nothing(); } params->length = args[offset + 3].As()->Value(); if (params->length < 0) { THROW_ERR_OUT_OF_RANGE(env, "length must be <= %d", INT_MAX); return Nothing(); } Utf8Value name(args.GetIsolate(), args[offset + 4]); params->digest = EVP_get_digestbyname(*name); if (params->digest == nullptr) { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *name); return Nothing(); } return Just(true); } bool PBKDF2Traits::DeriveBits( Environment* env, const PBKDF2Config& params, ByteSource* out) { char* data = MallocOpenSSL(params.length); ByteSource buf = ByteSource::Allocated(data, params.length); unsigned char* ptr = reinterpret_cast(data); // Both pass and salt may be zero length here. // The generated bytes are stored in buf, which is // assigned to out on success. if (PKCS5_PBKDF2_HMAC( params.pass.get(), params.pass.size(), params.salt.data(), params.salt.size(), params.iterations, params.digest, params.length, ptr) <= 0) { return false; } *out = std::move(buf); return true; } } // namespace crypto } // namespace node