git ssb

0+

dangerousbeans / %aPBe2k3ugtjBr4rrsU1…



Commit 8caa1cce2f45c862ce127c2cbdeea0490b55d57f

When on Ruby 1.9, unlock the global interpreter lock while calculating bcrypt hashes, for greater concurrency.

The bcrypt sources have been modified and made reentrant so that they
don't cause any problems when accessed by multiple threads concurrently.

Signed-off-by: Coda Hale <coda.hale@gmail.com>
Hongli Lai (Phusion) authored on 8/5/2009, 7:21:03 AM
Coda Hale committed on 8/12/2009, 9:55:36 PM
Parent: 8f7acf49d55b45dbc448f40547d9087762263ddb

Files changed

Rakefilechanged
ext/bcrypt.cchanged
ext/bcrypt_ext.cchanged
ext/bcrypt.hadded
RakefileView
@@ -113,9 +113,9 @@
113113 desc "Run a set of benchmarks on the compiled extension."
114114 task :benchmark do
115115 TESTS = 100
116116 TEST_PWD = "this is a test"
117- require "lib/bcrypt"
117+ require File.expand_path(File.join(File.dirname(__FILE__), "lib", "bcrypt"))
118118 Benchmark.bmbm do |results|
119119 4.upto(10) do |n|
120120 results.report("cost #{n}:") { TESTS.times { BCrypt::Password.create(TEST_PWD, :cost => n) } }
121121 end
ext/bcrypt.cView
@@ -1,7 +1,12 @@
11 /* $OpenBSD: bcrypt.c,v 1.22 2007/02/20 01:44:16 ray Exp $ */
22
33 /*
4+ * Modified by <hongli@phusion.nl> on 2009-08-05:
5+ *
6+ * - Got rid of the global variables; they're not thread-safe.
7+ * Modified the functions to accept local buffers instead.
8+ *
49 * Modified by <coda.hale@gmail.com> on 2007-02-27:
510 *
611 * - Changed bcrypt_gensalt to accept a random seed as a parameter,
712 * to remove the code's dependency on arc4random(), which isn't
@@ -61,29 +66,19 @@
6166 #include <stdlib.h>
6267 #include <sys/types.h>
6368 #include <string.h>
6469 #include "blf.h"
70+#include "bcrypt.h"
6571
6672 /* This implementation is adaptable to current computing power.
6773 * You can have up to 2^31 rounds which should be enough for some
6874 * time to come.
6975 */
7076
71-#define BCRYPT_VERSION '2'
72-#define BCRYPT_MAXSALT 16 /* Precomputation is just so nice */
73-#define BCRYPT_BLOCKS 6 /* Ciphertext blocks */
74-#define BCRYPT_MINROUNDS 16 /* we have log2(rounds) in salt */
75-
76-char *bcrypt_gensalt(u_int8_t, u_int8_t *);
77-
7877 static void encode_salt(char *, u_int8_t *, u_int16_t, u_int8_t);
7978 static void encode_base64(u_int8_t *, u_int8_t *, u_int16_t);
8079 static void decode_base64(u_int8_t *, u_int16_t, u_int8_t *);
8180
82-static char encrypted[_PASSWORD_LEN];
83-static char gsalt[7 + (BCRYPT_MAXSALT * 4 + 2) / 3 + 1];
84-static char error[] = ":";
85-
8681 const static u_int8_t Base64Code[] =
8782 "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
8883
8984 const static u_int8_t index_64[128] = {
@@ -154,24 +149,24 @@
154149 Since versions may change. Keeping this here
155150 seems sensible.
156151 */
157152
158-char *
159-bcrypt_gensalt(u_int8_t log_rounds, u_int8_t *rseed)
153+char *
154+bcrypt_gensalt(char *output, u_int8_t log_rounds, u_int8_t *rseed)
160155 {
161156 if (log_rounds < 4)
162157 log_rounds = 4;
163158 else if (log_rounds > 31)
164159 log_rounds = 31;
165160
166- encode_salt(gsalt, rseed, BCRYPT_MAXSALT, log_rounds);
167- return gsalt;
161+ encode_salt(output, rseed, BCRYPT_MAXSALT, log_rounds);
162+ return output;
168163 }
169164 /* We handle $Vers$log2(NumRounds)$salt+passwd$
170165 i.e. $2$04$iwouldntknowwhattosayetKdJ6iFtacBqJdKe6aW7ou */
171166
172167 char *
173-bcrypt(const char *key, const char *salt)
168+bcrypt(char *output, const char *key, const char *salt)
174169 {
175170 blf_ctx state;
176171 u_int32_t rounds, i, k;
177172 u_int16_t j;
@@ -184,10 +179,9 @@
184179 /* Discard "$" identifier */
185180 salt++;
186181
187182 if (*salt > BCRYPT_VERSION) {
188- /* How do I handle errors ? Return ':' */
189- return error;
183+ return NULL;
190184 }
191185
192186 /* Check for minor versions */
193187 if (salt[1] != '$') {
@@ -197,9 +191,9 @@
197191 minor = salt[1];
198192 salt++;
199193 break;
200194 default:
201- return error;
195+ return NULL;
202196 }
203197 } else
204198 minor = 0;
205199
@@ -207,23 +201,23 @@
207201 salt += 2;
208202
209203 if (salt[2] != '$')
210204 /* Out of sync with passwd entry */
211- return error;
205+ return NULL;
212206
213207 /* Computer power doesn't increase linear, 2^x should be fine */
214208 n = atoi(salt);
215209 if (n > 31 || n < 0)
216- return error;
210+ return NULL;
217211 logr = (u_int8_t)n;
218212 if ((rounds = (u_int32_t) 1 << logr) < BCRYPT_MINROUNDS)
219- return error;
213+ return NULL;
220214
221215 /* Discard num rounds + "$" identifier */
222216 salt += 3;
223217
224218 if (strlen(salt) * 3 / 4 < BCRYPT_MAXSALT)
225- return error;
219+ return NULL;
226220
227221 /* We dont want the base64 salt but the raw data */
228222 decode_base64(csalt, BCRYPT_MAXSALT, (u_int8_t *) salt);
229223 salt_len = BCRYPT_MAXSALT;
@@ -258,20 +252,20 @@
258252 }
259253
260254
261255 i = 0;
262- encrypted[i++] = '$';
263- encrypted[i++] = BCRYPT_VERSION;
256+ output[i++] = '$';
257+ output[i++] = BCRYPT_VERSION;
264258 if (minor)
265- encrypted[i++] = minor;
266- encrypted[i++] = '$';
259+ output[i++] = minor;
260+ output[i++] = '$';
267261
268- snprintf(encrypted + i, 4, "%2.2u$", logr);
262+ snprintf(output + i, 4, "%2.2u$", logr);
269263
270- encode_base64((u_int8_t *) encrypted + i + 3, csalt, BCRYPT_MAXSALT);
271- encode_base64((u_int8_t *) encrypted + strlen(encrypted), ciphertext,
264+ encode_base64((u_int8_t *) output + i + 3, csalt, BCRYPT_MAXSALT);
265+ encode_base64((u_int8_t *) output + strlen(output), ciphertext,
272266 4 * BCRYPT_BLOCKS - 1);
273- return encrypted;
267+ return output;
274268 }
275269
276270 static void
277271 encode_base64(u_int8_t *buffer, u_int8_t *data, u_int16_t len)
ext/bcrypt_ext.cView
@@ -1,33 +1,78 @@
11 #include "ruby.h"
2-#include "blf.h"
2+#include "bcrypt.h"
33
4-char *bcrypt_gensalt(u_int8_t, u_int8_t *);
5-char *bcrypt(const char *, const char *);
4+static VALUE mBCrypt;
5+static VALUE cBCryptEngine;
66
7-VALUE mBCrypt;
8-VALUE cBCryptEngine;
9-
107 /* Define RSTRING_PTR for Ruby 1.8.5, ruby-core's idea of a point release is
118 insane. */
129 #ifndef RSTRING_PTR
1310 # define RSTRING_PTR(s) (RSTRING(s)->ptr)
1411 #endif
1512
13+#ifdef RUBY_VM
14+# define RUBY_1_9
15+#endif
16+
17+#ifdef RUBY_1_9
18+
19+ /* When on Ruby 1.9+, we will want to unlock the GIL while performing
20+ * expensive calculations, for greater concurrency.
21+ */
22+
23+ typedef struct {
24+ char *output;
25+ const char *key;
26+ const char *salt;
27+ } BCryptArguments;
28+
29+ static VALUE bcrypt_wrapper(void *_args) {
30+ BCryptArguments *args = (BCryptArguments *)_args;
31+ return (VALUE)bcrypt(args->output, args->key, args->salt);
32+ }
33+
34+#endif /* RUBY_1_9 */
35+
1636 /* Given a logarithmic cost parameter, generates a salt for use with +bc_crypt+.
1737 */
1838 static VALUE bc_salt(VALUE self, VALUE cost, VALUE seed) {
19- return rb_str_new2((char *)bcrypt_gensalt(NUM2INT(cost), (u_int8_t *)RSTRING_PTR(seed)));
39+ int icost = NUM2INT(cost);
40+ char salt[BCRYPT_SALT_OUTPUT_SIZE];
41+
42+ bcrypt_gensalt(salt, icost, (u_int8_t *)RSTRING_PTR(seed));
43+ return rb_str_new2(salt);
2044 }
2145
2246 /* Given a secret and a salt, generates a salted hash (which you can then store safely).
2347 */
2448 static VALUE bc_crypt(VALUE self, VALUE key, VALUE salt) {
2549 const char * safeguarded = RSTRING_PTR(key) ? RSTRING_PTR(key) : "";
26- return rb_str_new2((char *)bcrypt(safeguarded, (char *)RSTRING_PTR(salt)));
50+ char output[BCRYPT_OUTPUT_SIZE];
51+
52+ #ifdef RUBY_1_9
53+ BCryptArguments args;
54+ VALUE ret;
55+
56+ args.output = output;
57+ args.key = safeguarded;
58+ args.salt = RSTRING_PTR(salt);
59+ ret = rb_thread_blocking_region(bcrypt_wrapper, &args, RUBY_UBF_IO, 0);
60+ if (ret != (VALUE) 0) {
61+ return rb_str_new2(output);
62+ } else {
63+ return Qnil;
64+ }
65+ #else
66+ if (bcrypt(output, safeguarded, (char *)RSTRING_PTR(salt)) != NULL) {
67+ return rb_str_new2(output);
68+ } else {
69+ return Qnil;
70+ }
71+ #endif
2772 }
2873
29-/* Create the BCrypt and BCrypt::Internals modules, and populate them with methods. */
74+/* Create the BCrypt and BCrypt::Engine modules, and populate them with methods. */
3075 void Init_bcrypt_ext(){
3176 mBCrypt = rb_define_module("BCrypt");
3277 cBCryptEngine = rb_define_class_under(mBCrypt, "Engine", rb_cObject);
3378
ext/bcrypt.hView
@@ -1,0 +1,65 @@
1+/*
2+ * Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
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+ * 1. Redistributions of source code must retain the above copyright
9+ * notice, this list of conditions and the following disclaimer.
10+ * 2. Redistributions in binary form must reproduce the above copyright
11+ * notice, this list of conditions and the following disclaimer in the
12+ * documentation and/or other materials provided with the distribution.
13+ * 3. All advertising materials mentioning features or use of this software
14+ * must display the following acknowledgement:
15+ * This product includes software developed by Niels Provos.
16+ * 4. The name of the author may not be used to endorse or promote products
17+ * derived from this software without specific prior written permission.
18+ *
19+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24+ * NOT 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 OF
28+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+ */
30+
31+#ifndef _BCRYPT_H_
32+#define _BCRYPT_H_
33+
34+#define BCRYPT_VERSION '2'
35+#define BCRYPT_MAXSALT 16 /* Precomputation is just so nice */
36+#define BCRYPT_BLOCKS 6 /* Ciphertext blocks */
37+#define BCRYPT_MINROUNDS 16 /* we have log2(rounds) in salt */
38+#define BCRYPT_SALT_OUTPUT_SIZE (7 + (BCRYPT_MAXSALT * 4 + 2) / 3 + 1)
39+#define BCRYPT_OUTPUT_SIZE 128
40+
41+/*
42+ * Given a logarithmic cost parameter, generates a salt for use with bcrypt().
43+ *
44+ * output: the computed salt will be stored here. This buffer must be
45+ * at least BCRYPT_SALT_OUTPUT_SIZE bytes. The result will be
46+ * null-terminated.
47+ * log_rounds: the logarithmic cost.
48+ * rseed: a seed of BCRYPT_MAXSALT bytes. Should be obtained from a
49+ * cryptographically secure random source.
50+ * Returns: output
51+ */
52+char *bcrypt_gensalt(char *output, u_int8_t log_rounds, u_int8_t *rseed);
53+
54+/*
55+ * Given a secret and a salt, generates a salted hash (which you can then store safely).
56+ *
57+ * output: the computed salted hash will be stored here. This buffer must
58+ * be at least BCRYPT_OUTPUT_SIZE bytes, and will become null-terminated.
59+ * key: A null-terminated secret.
60+ * salt: The salt, as generated by bcrypt_gensalt().
61+ * Returns: output on success, NULL on error.
62+ */
63+char *bcrypt(char *output, const char *key, const char *salt);
64+
65+#endif /* _BCRYPT_H_ */

Built with git-ssb-web