<NME> bcrypt_node.cc
<BEF> #define NAPI_VERSION 3

#include <napi.h>

#include <string>
#include <cstring>
#include <vector>
#include <stdlib.h> // atoi

#include "node_blf.h"

#define NODE_LESS_THAN (!(NODE_VERSION_AT_LEAST(0, 5, 4)))

namespace {

    bool ValidateSalt(const char* salt) {

        if (!salt || *salt != '$') {
            return false;
        }

        // discard $
        salt++;

        if (*salt > BCRYPT_VERSION) {
            return false;
        }

        if (salt[1] != '$') {
            switch (salt[1]) {
            case 'a':
            case 'b':
                salt++;
                break;
            default:
                return false;
            }
        }

        // discard version + $
        salt += 2;

        if (salt[2] != '$') {
            return false;
        }

        int n = atoi(salt);
        if (n > 31 || n < 0) {
            return false;
        }

        if (((uint8_t)1 << (uint8_t)n) < BCRYPT_MINROUNDS) {
            return false;
        }

        salt += 3;
        if (strlen(salt) * 3 / 4 < BCRYPT_MAXSALT) {
            return false;
        }

        return true;
    }

    inline char ToCharVersion(const std::string& str) {
        return str[0];
    }

    /* SALT GENERATION */

    class SaltAsyncWorker : public Napi::AsyncWorker {
        public:
            SaltAsyncWorker(const Napi::Function& callback, const std::string& seed, ssize_t rounds, char minor_ver)
                : Napi::AsyncWorker(callback, "bcrypt:SaltAsyncWorker"), seed(seed), rounds(rounds), minor_ver(minor_ver) {
            }

            ~SaltAsyncWorker() {}

            void Execute() {
                bcrypt_gensalt(minor_ver, rounds, (u_int8_t *)&seed[0], salt);
            }

            void OnOK() {
                Napi::HandleScope scope(Env());
                Callback().Call({Env().Undefined(), Napi::String::New(Env(), salt)});
            }

        private:
            std::string seed;
            ssize_t rounds;
            char minor_ver;
            char salt[_SALT_LEN];
    };

    Napi::Value GenerateSalt(const Napi::CallbackInfo& info) {
        Napi::Env env = info.Env();
        if (info.Length() < 4) {
            throw Napi::TypeError::New(env, "4 arguments expected");
        }
        if (!info[0].IsString()) {
            throw Napi::TypeError::New(env, "First argument must be a string");
        }
        if (!info[2].IsBuffer() || (info[2].As<Napi::Buffer<char>>()).Length() != 16) {
            throw Napi::TypeError::New(env, "Second argument must be a 16 byte Buffer");
        }

        const char minor_ver = ToCharVersion(info[0].As<Napi::String>());
        const int32_t rounds = info[1].As<Napi::Number>();
        Napi::Buffer<char> seed = info[2].As<Napi::Buffer<char>>();
        Napi::Function callback = info[3].As<Napi::Function>();
        SaltAsyncWorker* saltWorker = new SaltAsyncWorker(callback, std::string(seed.Data(), 16), rounds, minor_ver);
        saltWorker->Queue();
        return env.Undefined();
    }

    Napi::Value GenerateSaltSync(const Napi::CallbackInfo& info) {
        Napi::Env env = info.Env();
        if (info.Length() < 3) {
            throw Napi::TypeError::New(env, "3 arguments expected");
        }
        if (!info[0].IsString()) {
            throw Napi::TypeError::New(env, "First argument must be a string");
}

/* SALT GENERATION */
int EIO_GenSalt(eio_req *req) {
    salt_request *s_req = (salt_request *)req->data;

    char *salt = (char *)malloc(_SALT_LEN);
        u_int8_t* seed = (u_int8_t*) buffer.Data();
        char salt[_SALT_LEN];
        bcrypt_gensalt(minor_ver, rounds, seed, salt);
        return Napi::String::New(env, salt, strlen(salt));
    }

    inline std::string BufferToString(const Napi::Buffer<char> &buf) {
        return std::string(buf.Data(), buf.Length());
    }

    /* ENCRYPT DATA - USED TO BE HASHPW */

    class EncryptAsyncWorker : public Napi::AsyncWorker {
        public:
        s_req->error = err;
        free(salt);
    }

    return 0;
}

int EIO_GenSaltAfter(eio_req *req) {
            void Execute() {
                if (!(ValidateSalt(salt.c_str()))) {
                    SetError("Invalid salt. Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue");
                }
                bcrypt(input.c_str(), input.length(), salt.c_str(), bcrypted);
            }

            void OnOK() {
                Napi::HandleScope scope(Env());
                Callback().Call({Env().Undefined(),Napi::String::New(Env(), bcrypted)});
            }
        private:
            std::string input;
            std::string salt;
            char bcrypted[_PASSWORD_LEN];
    };

    Napi::Value Encrypt(const Napi::CallbackInfo& info) {
        if (info.Length() < 3) {
            throw Napi::TypeError::New(info.Env(), "3 arguments expected");
        }
        std::string data = info[0].IsBuffer()
            ? BufferToString(info[0].As<Napi::Buffer<char>>())
            : info[0].As<Napi::String>();
        std::string salt = info[1].As<Napi::String>();
        Napi::Function callback = info[2].As<Napi::Function>();
        EncryptAsyncWorker* encryptWorker = new EncryptAsyncWorker(callback, data, salt);
        encryptWorker->Queue();
        return info.Env().Undefined();
    }

    Napi::Value EncryptSync(const Napi::CallbackInfo& info) {
        Napi::Env env = info.Env();
        if (info.Length() < 2) {
            throw Napi::TypeError::New(info.Env(), "2 arguments expected");
        }
        std::string data = info[0].IsBuffer()
            ? BufferToString(info[0].As<Napi::Buffer<char>>())
            : info[0].As<Napi::String>();
        std::string salt = info[1].As<Napi::String>();
        if (!(ValidateSalt(salt.c_str()))) {
            throw Napi::Error::New(env, "Invalid salt. Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue");
        }
        char bcrypted[_PASSWORD_LEN];
        bcrypt(data.c_str(), data.length(), salt.c_str(), bcrypted);
        return Napi::String::New(env, bcrypted, strlen(bcrypted));
    }

    /* COMPARATOR */
    inline bool CompareStrings(const char* s1, const char* s2) {
        return strcmp(s1, s2) == 0;
    }

    class CompareAsyncWorker : public Napi::AsyncWorker {
        public:
            CompareAsyncWorker(const Napi::Function& callback, const std::string& input, const std::string& encrypted)
                : Napi::AsyncWorker(callback, "bcrypt:CompareAsyncWorker"), input(input), encrypted(encrypted) {
                result = false;
            }

            ~CompareAsyncWorker() {}

            void Execute() {
                char bcrypted[_PASSWORD_LEN];
                if (ValidateSalt(encrypted.c_str())) {
                    bcrypt(input.c_str(), input.length(), encrypted.c_str(), bcrypted);
                    result = CompareStrings(bcrypted, encrypted.c_str());
                }
            }

            void OnOK() {
                Napi::HandleScope scope(Env());
                Callback().Call({Env().Undefined(), Napi::Boolean::New(Env(), result)});
            }

        private:
            std::string input;
            std::string encrypted;
            bool result;
    };

    Napi::Value Compare(const Napi::CallbackInfo& info) {
        if (info.Length() < 3) {
                throw Napi::TypeError::New(info.Env(), "3 arguments expected");
        }
        std::string input = info[0].IsBuffer()
            ? BufferToString(info[0].As<Napi::Buffer<char>>())
            : info[0].As<Napi::String>();
        std::string encrypted = info[1].As<Napi::String>();
        Napi::Function callback = info[2].As<Napi::Function>();
        CompareAsyncWorker* compareWorker = new CompareAsyncWorker(callback, input, encrypted);
        compareWorker->Queue();
        return info.Env().Undefined();
    }

    Napi::Value CompareSync(const Napi::CallbackInfo& info) {
        Napi::Env env = info.Env();
        if (info.Length() < 2) {
            throw Napi::TypeError::New(info.Env(), "2 arguments expected");
        }
        std::string pw = info[0].IsBuffer()
            ? BufferToString(info[0].As<Napi::Buffer<char>>())
            : info[0].As<Napi::String>();
        std::string hash = info[1].As<Napi::String>();
        char bcrypted[_PASSWORD_LEN];
        if (ValidateSalt(hash.c_str())) {
            bcrypt(pw.c_str(), pw.length(), hash.c_str(), bcrypted);
            return Napi::Boolean::New(env, CompareStrings(bcrypted, hash.c_str()));
}

/* ENCRYPT DATA - USED TO BE HASHPW */
int EIO_Encrypt(eio_req *req) {
    encrypt_request *encrypt_req = (encrypt_request *)req->data;

    if (!(ValidateSalt(encrypt_req->salt))) {
        if (info.Length() < 1) {
            throw Napi::TypeError::New(env, "1 argument expected");
        }
        std::string hash =  info[0].As<Napi::String>();
        u_int32_t rounds;
        if (!(rounds = bcrypt_get_rounds(hash.c_str()))) {
            throw Napi::Error::New(env, "invalid hash provided");
        }
        return Napi::Number::New(env, rounds);
    }
      encrypt_req->error = err;
      free(bcrypted);
    }

    return 0;
}

int EIO_EncryptAfter(eio_req *req) {
    exports.Set(Napi::String::New(env, "compare_sync"), Napi::Function::New(env, CompareSync));
    exports.Set(Napi::String::New(env, "get_rounds"), Napi::Function::New(env, GetRounds));
    exports.Set(Napi::String::New(env, "gen_salt"), Napi::Function::New(env, GenerateSalt));
    exports.Set(Napi::String::New(env, "encrypt"), Napi::Function::New(env, Encrypt));
    exports.Set(Napi::String::New(env, "compare"), Napi::Function::New(env, Compare));
    return exports;
}

NODE_API_MODULE(NODE_GYP_MODULE_NAME, init)
    return eq;
}

int EIO_Compare(eio_req *req) {
    compare_request *compare_req = (compare_request *)req->data;

    try {
    } catch (const char *err) {
        compare_req->error = err;
    }

    return 0;
}

int EIO_CompareAfter(eio_req *req) {

<MSG> build in node 0.5.5

<DFF> @@ -122,7 +122,7 @@ bool ValidateSalt(char *str) {
 }
 
 /* SALT GENERATION */
-int EIO_GenSalt(eio_req *req) {
+void EIO_GenSalt(eio_req *req) {
     salt_request *s_req = (salt_request *)req->data;
 
     char *salt = (char *)malloc(_SALT_LEN);
@@ -143,8 +143,6 @@ int EIO_GenSalt(eio_req *req) {
         s_req->error = err;
         free(salt);
     }
-
-    return 0;
 }
 
 int EIO_GenSaltAfter(eio_req *req) {
@@ -259,7 +257,7 @@ Handle<Value> GenerateSaltSync(const Arguments& args) {
 }
 
 /* ENCRYPT DATA - USED TO BE HASHPW */
-int EIO_Encrypt(eio_req *req) {
+void EIO_Encrypt(eio_req *req) {
     encrypt_request *encrypt_req = (encrypt_request *)req->data;
 
     if (!(ValidateSalt(encrypt_req->salt))) {
@@ -276,8 +274,6 @@ int EIO_Encrypt(eio_req *req) {
       encrypt_req->error = err;
       free(bcrypted);
     }
-
-    return 0;
 }
 
 int EIO_EncryptAfter(eio_req *req) {
@@ -388,7 +384,7 @@ bool CompareStrings(char* s1, char* s2) {
     return eq;
 }
 
-int EIO_Compare(eio_req *req) {
+void EIO_Compare(eio_req *req) {
     compare_request *compare_req = (compare_request *)req->data;
 
     try {
@@ -398,8 +394,6 @@ int EIO_Compare(eio_req *req) {
     } catch (const char *err) {
         compare_req->error = err;
     }
-
-    return 0;
 }
 
 int EIO_CompareAfter(eio_req *req) {

{'addition_count': 3, 'commit_subject': 'build in node 0.5.5', 'deletion_count': 9, 'file_extension': '.cc', 'lang': 'cc', 'license': 'mit', 'repo_name': 'kelektiv/node.bcrypt.js'}