#include #include #include #include #include "../argon2/include/argon2.h" #include using namespace Napi; #ifndef _MSC_VER namespace { #endif using ustring = std::basic_string; const char* type2string(argon2_type type) { return argon2_type2string(type, false); }; ustring from_buffer(const Value& value) { const auto& buf = value.As>(); return {buf.Data(), buf.Length()}; } Buffer to_buffer(const Env& env, const ustring& str) { return Buffer::Copy(env, str.data(), str.size()); } struct Options { // TODO: remove ctors and initializers when GCC<5 stops shipping Options(Options&&) = default; ustring secret; ustring ad; uint32_t hash_length; uint32_t time_cost; uint32_t memory_cost; uint32_t parallelism; uint32_t version; argon2_type type; }; argon2_context make_context(uint8_t* buf, const ustring& plain, const ustring& salt, const Options& opts) { argon2_context ctx; ctx.out = buf; ctx.outlen = opts.hash_length; ctx.pwd = const_cast(plain.data()); ctx.pwdlen = plain.size(); ctx.salt = const_cast(salt.data()); ctx.saltlen = salt.length(); ctx.secret = opts.secret.empty() ? nullptr : const_cast(opts.secret.data()); ctx.secretlen = opts.secret.size(); ctx.ad = opts.ad.empty() ? nullptr : const_cast(opts.ad.data()); ctx.adlen = opts.ad.size(); ctx.t_cost = opts.time_cost; ctx.m_cost = opts.memory_cost; ctx.lanes = opts.parallelism; ctx.threads = opts.parallelism; ctx.allocate_cbk = nullptr; ctx.free_cbk = nullptr; ctx.flags = ARGON2_DEFAULT_FLAGS; ctx.version = opts.version; return ctx; } class HashWorker final: public AsyncWorker { public: HashWorker(const Function& callback, ustring&& plain, ustring&& salt, Options&& opts): // TODO: use brackets when GCC <5 stops shipping AsyncWorker(callback, "argon2:HashWorker"), plain(std::move(plain)), salt(std::move(salt)), opts(std::move(opts)) {} void Execute() override { uint8_t* buf = new uint8_t[opts.hash_length]; auto ctx = make_context(buf, plain, salt, opts); int result = argon2_ctx(&ctx, opts.type); if (result != ARGON2_OK) { /* LCOV_EXCL_START */ SetError(argon2_error_message(result)); /* LCOV_EXCL_STOP */ } else { hash.assign(buf, opts.hash_length); } std::fill_n(buf, opts.hash_length, 0); delete[] buf; } void OnOK() override { const auto& env = Env(); HandleScope scope{env}; Callback()({env.Undefined(), to_buffer(env, hash)}); } private: ustring plain; ustring salt; Options opts; ustring hash; }; Options extract_opts(const Object& opts) { return { opts.Has("secret") ? from_buffer(opts["secret"]) : ustring{}, opts.Has("associatedData") ? from_buffer(opts["associatedData"]) : ustring{}, opts["hashLength"].ToNumber(), opts["timeCost"].ToNumber(), opts["memoryCost"].ToNumber(), opts["parallelism"].ToNumber(), opts["version"].ToNumber(), argon2_type(int(opts["type"].ToNumber())), }; } #ifndef _MSC_VER } #endif Value Hash(const CallbackInfo& info) { assert(info.Length() == 4 and info[0].IsBuffer() and info[1].IsBuffer() and info[2].IsObject() and info[3].IsFunction()); auto worker = new HashWorker{ info[3].As(), from_buffer(info[0]), from_buffer(info[1]), extract_opts(info[2].As()) }; worker->Queue(); return info.Env().Undefined(); } constexpr uint32_t max(uint32_t a, uint32_t b) { return a > b ? a : b; } Object init(Env env, Object exports) { auto limits = Object::New(env); const auto& setMaxMin = [&](const char* name, uint32_t max, uint32_t min) { auto obj = Object::New(env); obj["max"] = max; obj["min"] = min; limits[name] = obj; }; setMaxMin("hashLength", ARGON2_MAX_OUTLEN, ARGON2_MIN_OUTLEN); setMaxMin("memoryCost", ARGON2_MAX_MEMORY, max(ARGON2_MIN_MEMORY, 2048)); setMaxMin("timeCost", ARGON2_MAX_TIME, max(ARGON2_MIN_TIME, 2)); setMaxMin("parallelism", ARGON2_MAX_LANES, ARGON2_MIN_LANES); auto types = Object::New(env); types[type2string(Argon2_d)] = uint32_t(Argon2_d); types[type2string(Argon2_i)] = uint32_t(Argon2_i); types[type2string(Argon2_id)] = uint32_t(Argon2_id); auto names = Object::New(env); names[uint32_t(Argon2_d)] = type2string(Argon2_d); names[uint32_t(Argon2_i)] = type2string(Argon2_i); names[uint32_t(Argon2_id)] = type2string(Argon2_id); exports["limits"] = limits; exports["types"] = types; exports["names"] = names; exports["version"] = int(ARGON2_VERSION_NUMBER); exports["hash"] = Function::New(env, Hash); return exports; } NODE_API_MODULE(argon2_lib, init);