bcrypt_node.cc 8.5 KB

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