/ node_modules / bcrypt / src / bcrypt_node.cc
bcrypt_node.cc
  1  #define NAPI_VERSION 3
  2  
  3  #include <napi.h>
  4  
  5  #include <string>
  6  #include <cstring>
  7  #include <vector>
  8  #include <stdlib.h> // atoi
  9  
 10  #include "node_blf.h"
 11  
 12  #define NODE_LESS_THAN (!(NODE_VERSION_AT_LEAST(0, 5, 4)))
 13  
 14  namespace {
 15  
 16      bool ValidateSalt(const char* salt) {
 17  
 18          if (!salt || *salt != '$') {
 19              return false;
 20          }
 21  
 22          // discard $
 23          salt++;
 24  
 25          if (*salt > BCRYPT_VERSION) {
 26              return false;
 27          }
 28  
 29          if (salt[1] != '$') {
 30              switch (salt[1]) {
 31              case 'a':
 32              case 'b':
 33                  salt++;
 34                  break;
 35              default:
 36                  return false;
 37              }
 38          }
 39  
 40          // discard version + $
 41          salt += 2;
 42  
 43          if (salt[2] != '$') {
 44              return false;
 45          }
 46  
 47          int n = atoi(salt);
 48          if (n > 31 || n < 0) {
 49              return false;
 50          }
 51  
 52          if (((uint8_t)1 << (uint8_t)n) < BCRYPT_MINROUNDS) {
 53              return false;
 54          }
 55  
 56          salt += 3;
 57          if (strlen(salt) * 3 / 4 < BCRYPT_MAXSALT) {
 58              return false;
 59          }
 60  
 61          return true;
 62      }
 63  
 64      inline char ToCharVersion(const std::string& str) {
 65          return str[0];
 66      }
 67  
 68      /* SALT GENERATION */
 69  
 70      class SaltAsyncWorker : public Napi::AsyncWorker {
 71          public:
 72              SaltAsyncWorker(const Napi::Function& callback, const std::string& seed, ssize_t rounds, char minor_ver)
 73                  : Napi::AsyncWorker(callback, "bcrypt:SaltAsyncWorker"), seed(seed), rounds(rounds), minor_ver(minor_ver) {
 74              }
 75  
 76              ~SaltAsyncWorker() {}
 77  
 78              void Execute() {
 79                  bcrypt_gensalt(minor_ver, rounds, (u_int8_t *)&seed[0], salt);
 80              }
 81  
 82              void OnOK() {
 83                  Napi::HandleScope scope(Env());
 84                  Callback().Call({Env().Undefined(), Napi::String::New(Env(), salt)});
 85              }
 86  
 87          private:
 88              std::string seed;
 89              ssize_t rounds;
 90              char minor_ver;
 91              char salt[_SALT_LEN];
 92      };
 93  
 94      Napi::Value GenerateSalt(const Napi::CallbackInfo& info) {
 95          Napi::Env env = info.Env();
 96          if (info.Length() < 4) {
 97              throw Napi::TypeError::New(env, "4 arguments expected");
 98          }
 99          if (!info[0].IsString()) {
100              throw Napi::TypeError::New(env, "First argument must be a string");
101          }
102          if (!info[2].IsBuffer() || (info[2].As<Napi::Buffer<char>>()).Length() != 16) {
103              throw Napi::TypeError::New(env, "Second argument must be a 16 byte Buffer");
104          }
105  
106          const char minor_ver = ToCharVersion(info[0].As<Napi::String>());
107          const int32_t rounds = info[1].As<Napi::Number>();
108          Napi::Buffer<char> seed = info[2].As<Napi::Buffer<char>>();
109          Napi::Function callback = info[3].As<Napi::Function>();
110          SaltAsyncWorker* saltWorker = new SaltAsyncWorker(callback, std::string(seed.Data(), 16), rounds, minor_ver);
111          saltWorker->Queue();
112          return env.Undefined();
113      }
114  
115      Napi::Value GenerateSaltSync(const Napi::CallbackInfo& info) {
116          Napi::Env env = info.Env();
117          if (info.Length() < 3) {
118              throw Napi::TypeError::New(env, "3 arguments expected");
119          }
120          if (!info[0].IsString()) {
121              throw Napi::TypeError::New(env, "First argument must be a string");
122          }
123          if (!info[2].IsBuffer() || (info[2].As<Napi::Buffer<char>>()).Length() != 16) {
124              throw Napi::TypeError::New(env, "Third argument must be a 16 byte Buffer");
125          }
126          const char minor_ver = ToCharVersion(info[0].As<Napi::String>());
127          const int32_t rounds = info[1].As<Napi::Number>();
128          Napi::Buffer<u_int8_t> buffer = info[2].As<Napi::Buffer<u_int8_t>>();
129          u_int8_t* seed = (u_int8_t*) buffer.Data();
130          char salt[_SALT_LEN];
131          bcrypt_gensalt(minor_ver, rounds, seed, salt);
132          return Napi::String::New(env, salt, strlen(salt));
133      }
134  
135      inline std::string BufferToString(const Napi::Buffer<char> &buf) {
136          return std::string(buf.Data(), buf.Length());
137      }
138  
139      /* ENCRYPT DATA - USED TO BE HASHPW */
140  
141      class EncryptAsyncWorker : public Napi::AsyncWorker {
142          public:
143              EncryptAsyncWorker(const Napi::Function& callback, const std::string& input, const std::string& salt)
144                  : Napi::AsyncWorker(callback, "bcrypt:EncryptAsyncWorker"), input(input), salt(salt) {
145              }
146  
147              ~EncryptAsyncWorker() {}
148  
149              void Execute() {
150                  if (!(ValidateSalt(salt.c_str()))) {
151                      SetError("Invalid salt. Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue");
152                  }
153                  bcrypt(input.c_str(), input.length(), salt.c_str(), bcrypted);
154              }
155  
156              void OnOK() {
157                  Napi::HandleScope scope(Env());
158                  Callback().Call({Env().Undefined(),Napi::String::New(Env(), bcrypted)});
159              }
160          private:
161              std::string input;
162              std::string salt;
163              char bcrypted[_PASSWORD_LEN];
164      };
165  
166      Napi::Value Encrypt(const Napi::CallbackInfo& info) {
167          if (info.Length() < 3) {
168              throw Napi::TypeError::New(info.Env(), "3 arguments expected");
169          }
170          std::string data = info[0].IsBuffer()
171              ? BufferToString(info[0].As<Napi::Buffer<char>>())
172              : info[0].As<Napi::String>();
173          std::string salt = info[1].As<Napi::String>();
174          Napi::Function callback = info[2].As<Napi::Function>();
175          EncryptAsyncWorker* encryptWorker = new EncryptAsyncWorker(callback, data, salt);
176          encryptWorker->Queue();
177          return info.Env().Undefined();
178      }
179  
180      Napi::Value EncryptSync(const Napi::CallbackInfo& info) {
181          Napi::Env env = info.Env();
182          if (info.Length() < 2) {
183              throw Napi::TypeError::New(info.Env(), "2 arguments expected");
184          }
185          std::string data = info[0].IsBuffer()
186              ? BufferToString(info[0].As<Napi::Buffer<char>>())
187              : info[0].As<Napi::String>();
188          std::string salt = info[1].As<Napi::String>();
189          if (!(ValidateSalt(salt.c_str()))) {
190              throw Napi::Error::New(env, "Invalid salt. Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue");
191          }
192          char bcrypted[_PASSWORD_LEN];
193          bcrypt(data.c_str(), data.length(), salt.c_str(), bcrypted);
194          return Napi::String::New(env, bcrypted, strlen(bcrypted));
195      }
196  
197      /* COMPARATOR */
198      inline bool CompareStrings(const char* s1, const char* s2) {
199          return strcmp(s1, s2) == 0;
200      }
201  
202      class CompareAsyncWorker : public Napi::AsyncWorker {
203          public:
204              CompareAsyncWorker(const Napi::Function& callback, const std::string& input, const std::string& encrypted)
205                  : Napi::AsyncWorker(callback, "bcrypt:CompareAsyncWorker"), input(input), encrypted(encrypted) {
206                  result = false;
207              }
208  
209              ~CompareAsyncWorker() {}
210  
211              void Execute() {
212                  char bcrypted[_PASSWORD_LEN];
213                  if (ValidateSalt(encrypted.c_str())) {
214                      bcrypt(input.c_str(), input.length(), encrypted.c_str(), bcrypted);
215                      result = CompareStrings(bcrypted, encrypted.c_str());
216                  }
217              }
218  
219              void OnOK() {
220                  Napi::HandleScope scope(Env());
221                  Callback().Call({Env().Undefined(), Napi::Boolean::New(Env(), result)});
222              }
223  
224          private:
225              std::string input;
226              std::string encrypted;
227              bool result;
228      };
229  
230      Napi::Value Compare(const Napi::CallbackInfo& info) {
231          if (info.Length() < 3) {
232                  throw Napi::TypeError::New(info.Env(), "3 arguments expected");
233          }
234          std::string input = info[0].IsBuffer()
235              ? BufferToString(info[0].As<Napi::Buffer<char>>())
236              : info[0].As<Napi::String>();
237          std::string encrypted = info[1].As<Napi::String>();
238          Napi::Function callback = info[2].As<Napi::Function>();
239          CompareAsyncWorker* compareWorker = new CompareAsyncWorker(callback, input, encrypted);
240          compareWorker->Queue();
241          return info.Env().Undefined();
242      }
243  
244      Napi::Value CompareSync(const Napi::CallbackInfo& info) {
245          Napi::Env env = info.Env();
246          if (info.Length() < 2) {
247              throw Napi::TypeError::New(info.Env(), "2 arguments expected");
248          }
249          std::string pw = info[0].IsBuffer()
250              ? BufferToString(info[0].As<Napi::Buffer<char>>())
251              : info[0].As<Napi::String>();
252          std::string hash = info[1].As<Napi::String>();
253          char bcrypted[_PASSWORD_LEN];
254          if (ValidateSalt(hash.c_str())) {
255              bcrypt(pw.c_str(), pw.length(), hash.c_str(), bcrypted);
256              return Napi::Boolean::New(env, CompareStrings(bcrypted, hash.c_str()));
257          } else {
258              return Napi::Boolean::New(env, false);
259          }
260      }
261  
262      Napi::Value GetRounds(const Napi::CallbackInfo& info) {
263          Napi::Env env = info.Env();
264          if (info.Length() < 1) {
265              throw Napi::TypeError::New(env, "1 argument expected");
266          }
267          std::string hash =  info[0].As<Napi::String>();
268          u_int32_t rounds;
269          if (!(rounds = bcrypt_get_rounds(hash.c_str()))) {
270              throw Napi::Error::New(env, "invalid hash provided");
271          }
272          return Napi::Number::New(env, rounds);
273      }
274  
275  } // anonymous namespace
276  
277  Napi::Object init(Napi::Env env, Napi::Object exports) {
278      exports.Set(Napi::String::New(env, "gen_salt_sync"), Napi::Function::New(env, GenerateSaltSync));
279      exports.Set(Napi::String::New(env, "encrypt_sync"), Napi::Function::New(env, EncryptSync));
280      exports.Set(Napi::String::New(env, "compare_sync"), Napi::Function::New(env, CompareSync));
281      exports.Set(Napi::String::New(env, "get_rounds"), Napi::Function::New(env, GetRounds));
282      exports.Set(Napi::String::New(env, "gen_salt"), Napi::Function::New(env, GenerateSalt));
283      exports.Set(Napi::String::New(env, "encrypt"), Napi::Function::New(env, Encrypt));
284      exports.Set(Napi::String::New(env, "compare"), Napi::Function::New(env, Compare));
285      return exports;
286  }
287  
288  NODE_API_MODULE(NODE_GYP_MODULE_NAME, init)