bcrypt_node.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. /*
  2. * Copyright (c) 2010, Nicholas Campbell <nicholas.j.campbell@gmail.com>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. Neither the name of the <ORGANIZATION> nor the names of its
  15. * contributors may be used to endorse or promote products derived from
  16. * this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22. * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. #include <node.h>
  31. #include <node_version.h>
  32. #include <node_buffer.h>
  33. #include <string>
  34. #include <cstring>
  35. #include <vector>
  36. #include <stdlib.h> // atoi
  37. #include "node_blf.h"
  38. #define NODE_LESS_THAN (!(NODE_VERSION_AT_LEAST(0, 5, 4)))
  39. using namespace v8;
  40. using namespace node;
  41. namespace {
  42. struct baton_base {
  43. v8::Persistent<v8::Function> callback;
  44. std::string error;
  45. virtual ~baton_base() {
  46. callback.Dispose();
  47. }
  48. };
  49. struct salt_baton : baton_base {
  50. std::string seed;
  51. std::string salt;
  52. ssize_t rounds;
  53. salt_baton() : salt(), rounds(0) {}
  54. };
  55. struct encrypt_baton : baton_base {
  56. std::string salt;
  57. std::string input;
  58. std::string output;
  59. };
  60. struct compare_baton : baton_base {
  61. std::string input;
  62. std::string encrypted;
  63. bool result;
  64. compare_baton(): input(), encrypted(), result(false) {}
  65. };
  66. bool ValidateSalt(const char* salt) {
  67. if (!salt || *salt != '$') {
  68. return false;
  69. }
  70. // discard $
  71. salt++;
  72. if (*salt > BCRYPT_VERSION) {
  73. return false;
  74. }
  75. if (salt[1] != '$') {
  76. switch (salt[1]) {
  77. case 'a':
  78. salt++;
  79. break;
  80. default:
  81. return false;
  82. }
  83. }
  84. // discard version + $
  85. salt += 2;
  86. if (salt[2] != '$') {
  87. return false;
  88. }
  89. int n = atoi(salt);
  90. if (n > 31 || n < 0) {
  91. return false;
  92. }
  93. if (((uint8_t)1 << (uint8_t)n) < BCRYPT_MINROUNDS) {
  94. return false;
  95. }
  96. salt += 3;
  97. if (strlen(salt) * 3 / 4 < BCRYPT_MAXSALT) {
  98. return false;
  99. }
  100. return true;
  101. }
  102. /* SALT GENERATION */
  103. void GenSaltAsync(uv_work_t* req) {
  104. salt_baton* baton = static_cast<salt_baton*>(req->data);
  105. char salt[_SALT_LEN];
  106. bcrypt_gensalt(baton->rounds, (u_int8_t *)&baton->seed[0], salt);
  107. baton->salt = std::string(salt);
  108. }
  109. void GenSaltAsyncAfter(uv_work_t* req) {
  110. HandleScope scope;
  111. salt_baton* baton = static_cast<salt_baton*>(req->data);
  112. delete req;
  113. Handle<Value> argv[2];
  114. if (!baton->error.empty()) {
  115. argv[0] = Exception::Error(String::New(baton->error.c_str()));
  116. argv[1] = Undefined();
  117. }
  118. else {
  119. argv[0] = Undefined();
  120. argv[1] = Encode(baton->salt.c_str(), baton->salt.size(), BINARY);
  121. }
  122. TryCatch try_catch; // don't quite see the necessity of this
  123. baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);
  124. if (try_catch.HasCaught())
  125. FatalException(try_catch);
  126. delete baton;
  127. }
  128. Handle<Value> GenerateSalt(const Arguments &args) {
  129. HandleScope scope;
  130. if (args.Length() < 3) {
  131. return ThrowException(Exception::TypeError(String::New("3 arguments expected")));
  132. }
  133. if (!Buffer::HasInstance(args[1]) || Buffer::Length(args[1].As<Object>()) != 16) {
  134. return ThrowException(Exception::TypeError(String::New("Second argument must be a 16 byte Buffer")));
  135. }
  136. const ssize_t rounds = args[0]->Int32Value();
  137. Local<Object> seed = args[1].As<Object>();
  138. Local<Function> callback = Local<Function>::Cast(args[2]);
  139. salt_baton* baton = new salt_baton();
  140. baton->seed = std::string(Buffer::Data(seed), 16);
  141. baton->callback = Persistent<Function>::New(callback);
  142. baton->rounds = rounds;
  143. uv_work_t* req = new uv_work_t;
  144. req->data = baton;
  145. uv_queue_work(uv_default_loop(), req, GenSaltAsync, (uv_after_work_cb)GenSaltAsyncAfter);
  146. return Undefined();
  147. }
  148. Handle<Value> GenerateSaltSync(const Arguments& args) {
  149. HandleScope scope;
  150. if (args.Length() < 2) {
  151. return ThrowException(Exception::TypeError(String::New("2 arguments expected")));
  152. }
  153. if (!Buffer::HasInstance(args[1]) || Buffer::Length(args[1].As<Object>()) != 16) {
  154. return ThrowException(Exception::TypeError(String::New("Second argument must be a 16 byte Buffer")));
  155. }
  156. const ssize_t rounds = args[0]->Int32Value();
  157. u_int8_t* seed = (u_int8_t*)Buffer::Data(args[1].As<Object>());
  158. char salt[_SALT_LEN];
  159. bcrypt_gensalt(rounds, seed, salt);
  160. return scope.Close(Encode(salt, strlen(salt), BINARY));
  161. }
  162. /* ENCRYPT DATA - USED TO BE HASHPW */
  163. void EncryptAsync(uv_work_t* req) {
  164. encrypt_baton* baton = static_cast<encrypt_baton*>(req->data);
  165. if (!(ValidateSalt(baton->salt.c_str()))) {
  166. baton->error = "Invalid salt. Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue";
  167. }
  168. char bcrypted[_PASSWORD_LEN];
  169. bcrypt(baton->input.c_str(), baton->salt.c_str(), bcrypted);
  170. baton->output = std::string(bcrypted);
  171. }
  172. void EncryptAsyncAfter(uv_work_t* req) {
  173. HandleScope scope;
  174. encrypt_baton* baton = static_cast<encrypt_baton*>(req->data);
  175. delete req;
  176. Handle<Value> argv[2];
  177. if (!baton->error.empty()) {
  178. argv[0] = Exception::Error(String::New(baton->error.c_str()));
  179. argv[1] = Undefined();
  180. }
  181. else {
  182. argv[0] = Undefined();
  183. argv[1] = Encode(baton->output.c_str(), baton->output.size(), BINARY);
  184. }
  185. TryCatch try_catch; // don't quite see the necessity of this
  186. baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);
  187. if (try_catch.HasCaught())
  188. FatalException(try_catch);
  189. delete baton;
  190. }
  191. Handle<Value> Encrypt(const Arguments& args) {
  192. HandleScope scope;
  193. if (args.Length() < 3) {
  194. return ThrowException(Exception::TypeError(String::New("3 arguments expected")));
  195. }
  196. String::Utf8Value data(args[0]->ToString());
  197. String::Utf8Value salt(args[1]->ToString());
  198. Local<Function> callback = Local<Function>::Cast(args[2]);
  199. encrypt_baton* baton = new encrypt_baton();
  200. baton->callback = Persistent<Function>::New(callback);
  201. baton->input = std::string(*data);
  202. baton->salt = std::string(*salt);
  203. uv_work_t* req = new uv_work_t;
  204. req->data = baton;
  205. uv_queue_work(uv_default_loop(), req, EncryptAsync, (uv_after_work_cb)EncryptAsyncAfter);
  206. return Undefined();
  207. }
  208. Handle<Value> EncryptSync(const Arguments& args) {
  209. HandleScope scope;
  210. if (args.Length() < 2) {
  211. return ThrowException(Exception::TypeError(String::New("2 arguments expected")));
  212. }
  213. String::Utf8Value data(args[0]->ToString());
  214. String::Utf8Value salt(args[1]->ToString());
  215. if (!(ValidateSalt(*salt))) {
  216. return ThrowException(Exception::Error(String::New("Invalid salt. Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue")));
  217. }
  218. char bcrypted[_PASSWORD_LEN];
  219. bcrypt(*data, *salt, bcrypted);
  220. return scope.Close(Encode(bcrypted, strlen(bcrypted), BINARY));
  221. }
  222. /* COMPARATOR */
  223. bool CompareStrings(const char* s1, const char* s2) {
  224. bool eq = true;
  225. int s1_len = strlen(s1);
  226. int s2_len = strlen(s2);
  227. if (s1_len != s2_len) {
  228. eq = false;
  229. }
  230. const int max_len = (s2_len < s1_len) ? s1_len : s2_len;
  231. // to prevent timing attacks, should check entire string
  232. // don't exit after found to be false
  233. for (int i = 0; i < max_len; ++i) {
  234. if (s1_len >= i && s2_len >= i && s1[i] != s2[i]) {
  235. eq = false;
  236. }
  237. }
  238. return eq;
  239. }
  240. void CompareAsync(uv_work_t* req) {
  241. compare_baton* baton = static_cast<compare_baton*>(req->data);
  242. char bcrypted[_PASSWORD_LEN];
  243. if (ValidateSalt(baton->encrypted.c_str())) {
  244. bcrypt(baton->input.c_str(), baton->encrypted.c_str(), bcrypted);
  245. baton->result = CompareStrings(bcrypted, baton->encrypted.c_str());
  246. }
  247. }
  248. void CompareAsyncAfter(uv_work_t* req) {
  249. HandleScope scope;
  250. compare_baton* baton = static_cast<compare_baton*>(req->data);
  251. delete req;
  252. Handle<Value> argv[2];
  253. if (!baton->error.empty()) {
  254. argv[0] = Exception::Error(String::New(baton->error.c_str()));
  255. argv[1] = Undefined();
  256. } else {
  257. argv[0] = Undefined();
  258. argv[1] = Boolean::New(baton->result);
  259. }
  260. TryCatch try_catch; // don't quite see the necessity of this
  261. baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);
  262. if (try_catch.HasCaught())
  263. FatalException(try_catch);
  264. // done with the baton
  265. // free the memory and callback
  266. delete baton;
  267. }
  268. Handle<Value> Compare(const Arguments& args) {
  269. HandleScope scope;
  270. if (args.Length() < 3) {
  271. return ThrowException(Exception::TypeError(String::New("3 arguments expected")));
  272. }
  273. String::Utf8Value input(args[0]->ToString());
  274. String::Utf8Value encrypted(args[1]->ToString());
  275. Local<Function> callback = Local<Function>::Cast(args[2]);
  276. compare_baton* baton = new compare_baton();
  277. baton->callback = Persistent<Function>::New(callback);
  278. baton->input = std::string(*input);
  279. baton->encrypted = std::string(*encrypted);
  280. uv_work_t* req = new uv_work_t;
  281. req->data = baton;
  282. uv_queue_work(uv_default_loop(), req, CompareAsync, (uv_after_work_cb)CompareAsyncAfter);
  283. return Undefined();
  284. }
  285. Handle<Value> CompareSync(const Arguments& args) {
  286. HandleScope scope;
  287. if (args.Length() < 2) {
  288. return ThrowException(Exception::TypeError(String::New("2 arguments expected")));
  289. }
  290. String::Utf8Value pw(args[0]->ToString());
  291. String::Utf8Value hash(args[1]->ToString());
  292. char bcrypted[_PASSWORD_LEN];
  293. if (ValidateSalt(*hash)) {
  294. bcrypt(*pw, *hash, bcrypted);
  295. return Boolean::New(CompareStrings(bcrypted, *hash));
  296. } else {
  297. return Boolean::New(false);
  298. }
  299. }
  300. Handle<Value> GetRounds(const Arguments& args) {
  301. HandleScope scope;
  302. if (args.Length() < 1) {
  303. return ThrowException(Exception::TypeError(String::New("1 argument expected")));
  304. }
  305. String::Utf8Value hash(args[0]->ToString());
  306. u_int32_t rounds;
  307. if (!(rounds = bcrypt_get_rounds(*hash))) {
  308. return ThrowException(Exception::Error(String::New("invalid hash provided")));
  309. }
  310. return Integer::New(rounds);
  311. }
  312. } // anonymous namespace
  313. // bind the bcrypt module
  314. extern "C" void init(Handle<Object> target) {
  315. HandleScope scope;
  316. NODE_SET_METHOD(target, "gen_salt_sync", GenerateSaltSync);
  317. NODE_SET_METHOD(target, "encrypt_sync", EncryptSync);
  318. NODE_SET_METHOD(target, "compare_sync", CompareSync);
  319. NODE_SET_METHOD(target, "get_rounds", GetRounds);
  320. NODE_SET_METHOD(target, "gen_salt", GenerateSalt);
  321. NODE_SET_METHOD(target, "encrypt", Encrypt);
  322. NODE_SET_METHOD(target, "compare", Compare);
  323. };
  324. NODE_MODULE(bcrypt_lib, init);