bcrypt-ruby 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bcrypt-ruby might be problematic. Click here for more details.

data/COPYING ADDED
@@ -0,0 +1,32 @@
1
+ Copyright 2007 Coda Hale <coda.hale@gmail.com>
2
+
3
+ This product includes software developed by Niels Provos.
4
+
5
+ All rights reserved.
6
+
7
+ Redistribution and use in source and binary forms, with or without
8
+ modification, are permitted provided that the following conditions
9
+ are met:
10
+ 1. Redistributions of source code must retain the above copyright
11
+ notice, this list of conditions and the following disclaimer.
12
+ 2. Redistributions in binary form must reproduce the above copyright
13
+ notice, this list of conditions and the following disclaimer in the
14
+ documentation and/or other materials provided with the distribution.
15
+ 3. All advertising materials mentioning features or use of this software
16
+ must display the following acknowledgement:
17
+
18
+ This product includes software developed by Niels Provos.
19
+
20
+ 4. The name of the author may not be used to endorse or promote products
21
+ derived from this software without specific prior written permission.
22
+
23
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README ADDED
@@ -0,0 +1,158 @@
1
+ = bcrypt-ruby
2
+
3
+ An easy way to keep your users' passwords secure.
4
+
5
+ == Why you should use bcrypt
6
+
7
+ If you store user passwords in the clear, then an attacker who steals a copy of your database has a giant list of emails
8
+ and passwords. Some of your users will only have one password -- for their email account, for their banking account, for
9
+ your application. A simple hack could escalate into massive identity theft.
10
+
11
+ It's your responsibility as a web developer to make your web application secure -- blaming your users for not being
12
+ security experts is not a professional response to risk.
13
+
14
+ bcrypt allows you to easily harden your application against these kinds of attacks.
15
+
16
+ == How to install bcrypt
17
+
18
+ sudo gem install bcrypt-ruby
19
+
20
+ You'll need a working compiler. (Win32 folks should use Cygwin or um, something else.)
21
+
22
+ == How to use bcrypt in your Rails application
23
+
24
+ === The +User+ model
25
+
26
+ require 'bcrypt'
27
+
28
+ class User < ActiveRecord::Base
29
+ # users.hash in the database is a :string
30
+ include BCrypt
31
+
32
+ def password
33
+ @password ||= Password.new(hash)
34
+ end
35
+
36
+ def password=(new_password)
37
+ @password = Password.create(new_password)
38
+ self.hash = @password
39
+ end
40
+
41
+ end
42
+
43
+ === Creating an account
44
+
45
+ def create
46
+ @user = User.new(params[:user])
47
+ @user.password = params[:password]
48
+ @user.save!
49
+ end
50
+
51
+ === Authenticating a user
52
+
53
+ def login
54
+ @user = User.find_by_email(params[:email])
55
+ if @user.password == params[:password]
56
+ give_token
57
+ else
58
+ redirect_to home_url
59
+ end
60
+ end
61
+
62
+ === If a user forgets their password?
63
+
64
+ # assign them a random one and mail it to them, asking them to change it
65
+ def forgot_password
66
+ @user = User.find_by_email(params[:email])
67
+ random_password = Array.new(10).map { (65 + rand(58)).chr }.join
68
+ @user.password = random_password
69
+ @user.save!
70
+ Mailer.create_and_deliver_password_change(@user, random_password)
71
+ end
72
+
73
+ == How to use bcrypt-ruby in general
74
+
75
+ require 'bcrypt'
76
+
77
+ my_password = BCrypt::Password.create("my password") #=> "$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa"
78
+
79
+ my_password.version #=> "2a"
80
+ my_password.cost #=> 10
81
+ my_password == "my password" #=> true
82
+ my_password == "not my password" #=> false
83
+
84
+ my_password = BCrypt::Password.new("$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa")
85
+ my_password == "my password" #=> true
86
+ my_password == "not my password" #=> false
87
+
88
+ Check the rdocs for more details -- BCrypt, BCrypt::Password.
89
+
90
+ == How bcrypt() works
91
+
92
+ bcrypt() is a hashing algorithm designed by Niels Provos and David Mazières of the OpenBSD Project.
93
+
94
+ Hash algorithms take a chunk of data (e.g., your user's password) and create a "digital fingerprint," or hash, of it.
95
+ Because this process is not reversible, there's no way to go from the hash back to the password.
96
+
97
+ In other words:
98
+
99
+ hash(p) #=> <unique gibberish>
100
+
101
+ You can store the hash and check it against a hash made of a potentially valid password:
102
+
103
+ <unique gibberish> =? hash(just_entered_password)
104
+
105
+ But even this has weaknesses -- attackers can just run lists of possible passwords through the same algorithm, store the
106
+ results in a big database, and then look up the passwords by their hash:
107
+
108
+ PrecomputedPassword.find_by_hash(<unique gibberish>).password #=> "secret1"
109
+
110
+ The solution to this is to add a small chunk of random data -- called a salt -- to the password before it's hashed:
111
+
112
+ hash(salt + p) #=> <really unique gibberish>
113
+
114
+ The salt is then stored along with the hash in the database, and used to check potentially valid passwords:
115
+
116
+ <really unique gibberish> =? hash(salt + just_entered_password)
117
+
118
+ bcrypt-ruby automatically handles the storage and generation of these salts for you.
119
+
120
+ Adding a salt means that an attacker has to have a gigantic database for each unique salt -- for a salt made of 4
121
+ letters, that's 456,976 different databases. Pretty much no one has that much storage space, so attackers try a
122
+ different, slower method -- throw a list of potential passwords at each individual password:
123
+
124
+ hash(salt + "aadvark") =? <really unique gibberish>
125
+ hash(salt + "abacus") =? <really unique gibberish>
126
+ etc.
127
+
128
+ This is much slower than the big database approach, but most hash algorithms are pretty quick -- and therein lies the
129
+ problem. Hash algorithms aren't usually designed to be slow, they're designed to turn gigabytes of data into secure
130
+ fingerprints as quickly as possible. bcrypt(), though, is designed to be computationally expensive:
131
+
132
+ Ten thousand iterations:
133
+ user system total real
134
+ md5 0.070000 0.000000 0.070000 ( 0.070415)
135
+ bcrypt 22.230000 0.080000 22.310000 ( 22.493822)
136
+
137
+ If an attacker was using Ruby to check each password, they could check ~140,000 passwords a second with MD5 but only
138
+ ~450 passwords a second with bcrypt().
139
+
140
+ In addition, bcrypt() allows you to increase the amount of work required to hash a password as computers get faster. Old
141
+ passwords will still work fine, but new passwords can keep up with the times.
142
+
143
+ bcrypt() is currently used as the default password storage hash in OpenBSD, widely regarded as the most secure operating
144
+ system available.
145
+
146
+
147
+ For a more technical explanation of the algorithm and its design criteria, please read Niels Provos and David Mazières'
148
+ Usenix99 paper:
149
+ http://www.usenix.org/events/usenix99/provos.html
150
+
151
+ If you'd like more down-to-earth advice regarding cryptography, I suggest reading <i>Practical Cryptography</i> by Niels
152
+ Ferguson and Bruce Schneier:
153
+ http://www.schneier.com/book-practical.html
154
+
155
+ = Etc
156
+
157
+ Author :: Coda Hale <coda.hale@gmail.com>
158
+ Website :: http://blog.codahale.com
data/Rakefile ADDED
@@ -0,0 +1,78 @@
1
+ gem "rspec"
2
+ require "spec/rake/spectask"
3
+ require 'rake/gempackagetask'
4
+ require 'rake/contrib/rubyforgepublisher'
5
+ require 'rake/clean'
6
+ require 'rake/rdoctask'
7
+
8
+ PKG_NAME = "bcrypt-ruby"
9
+ PKG_VERSION = "1.0.0"
10
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
11
+ PKG_FILES = FileList[
12
+ '[A-Z]*',
13
+ 'lib/**/*.rb',
14
+ 'spec/**/*.rb',
15
+ 'ext/*.c',
16
+ 'ext/*.h',
17
+ 'ext/*.rb'
18
+ ]
19
+
20
+ task :default => [:spec]
21
+
22
+ desc "Run all specs"
23
+ Spec::Rake::SpecTask.new do |t|
24
+ t.spec_files = FileList['spec/**/*_spec.rb']
25
+ t.spec_opts = ['--color','--backtrace','--diff']
26
+ t.rcov = true
27
+ t.rcov_dir = 'doc/output/coverage'
28
+ t.rcov_opts = ['--exclude', 'spec\/spec,spec\/.*_spec.rb']
29
+ end
30
+
31
+ desc "Run all specs and store html output in doc/output/report.html"
32
+ Spec::Rake::SpecTask.new('spec_html') do |t|
33
+ t.spec_files = FileList['spec/**/*_spec.rb']
34
+ t.spec_opts = ['--diff','--format html','--backtrace','--out doc/output/report.html']
35
+ end
36
+
37
+ desc 'Generate RDoc'
38
+ rd = Rake::RDocTask.new do |rdoc|
39
+ rdoc.rdoc_dir = 'doc/output/rdoc'
40
+ rdoc.options << '--title' << 'bcrypt-ruby' << '--line-numbers' << '--inline-source' << '--main' << 'README'
41
+ rdoc.template = ENV['TEMPLATE'] if ENV['TEMPLATE']
42
+ rdoc.rdoc_files.include('README', 'COPYING', 'lib/**/*.rb')
43
+ end
44
+
45
+ spec = Gem::Specification.new do |s|
46
+ s.name = PKG_NAME
47
+ s.version = PKG_VERSION
48
+ s.summary = "Blah."
49
+ s.description = <<-EOF
50
+ Woot.
51
+ EOF
52
+
53
+ s.files = PKG_FILES.to_a
54
+ s.require_path = 'lib'
55
+
56
+ s.has_rdoc = true
57
+ s.rdoc_options = rd.options
58
+ s.extra_rdoc_files = rd.rdoc_files.to_a
59
+
60
+ s.extensions = FileList["ext/extconf.rb"].to_a
61
+
62
+ s.autorequire = 'bcrypt'
63
+ s.author = ["Coda Hale"]
64
+ s.email = "coda.hale@gmail.com"
65
+ s.homepage = "http://bcrypt-ruby.rubyforge.org"
66
+ s.rubyforge_project = "bcrypt-ruby"
67
+ end
68
+
69
+ Rake::GemPackageTask.new(spec) do |pkg|
70
+ pkg.need_zip = true
71
+ pkg.need_tar = true
72
+ end
73
+
74
+ task :compile do
75
+ Dir.chdir('./ext')
76
+ system "ruby extconf.rb"
77
+ system "make"
78
+ end
data/ext/bcrypt.c ADDED
@@ -0,0 +1,334 @@
1
+ /* $OpenBSD: bcrypt.c,v 1.22 2007/02/20 01:44:16 ray Exp $ */
2
+
3
+ /*
4
+ * Modified by <coda.hale@gmail.com> on 2007-02-27:
5
+ *
6
+ * - Changed bcrypt_gensalt to accept a random seed as a parameter,
7
+ * to remove the code's dependency on arc4random(), which isn't
8
+ * available on Linux.
9
+ */
10
+
11
+ /*
12
+ * Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
13
+ * All rights reserved.
14
+ *
15
+ * Redistribution and use in source and binary forms, with or without
16
+ * modification, are permitted provided that the following conditions
17
+ * are met:
18
+ * 1. Redistributions of source code must retain the above copyright
19
+ * notice, this list of conditions and the following disclaimer.
20
+ * 2. Redistributions in binary form must reproduce the above copyright
21
+ * notice, this list of conditions and the following disclaimer in the
22
+ * documentation and/or other materials provided with the distribution.
23
+ * 3. All advertising materials mentioning features or use of this software
24
+ * must display the following acknowledgement:
25
+ * This product includes software developed by Niels Provos.
26
+ * 4. The name of the author may not be used to endorse or promote products
27
+ * derived from this software without specific prior written permission.
28
+ *
29
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
30
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
31
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
32
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
33
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
34
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
38
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
+ */
40
+
41
+ /* This password hashing algorithm was designed by David Mazieres
42
+ * <dm@lcs.mit.edu> and works as follows:
43
+ *
44
+ * 1. state := InitState ()
45
+ * 2. state := ExpandKey (state, salt, password) 3.
46
+ * REPEAT rounds:
47
+ * state := ExpandKey (state, 0, salt)
48
+ * state := ExpandKey(state, 0, password)
49
+ * 4. ctext := "OrpheanBeholderScryDoubt"
50
+ * 5. REPEAT 64:
51
+ * ctext := Encrypt_ECB (state, ctext);
52
+ * 6. RETURN Concatenate (salt, ctext);
53
+ *
54
+ */
55
+
56
+ #if 0
57
+ #include <stdio.h>
58
+ #endif
59
+
60
+ #include <stdio.h>
61
+ #include <stdlib.h>
62
+ #include <sys/types.h>
63
+ #include <string.h>
64
+ #include "blf.h"
65
+
66
+ /* This implementation is adaptable to current computing power.
67
+ * You can have up to 2^31 rounds which should be enough for some
68
+ * time to come.
69
+ */
70
+
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
+
78
+ static void encode_salt(char *, u_int8_t *, u_int16_t, u_int8_t);
79
+ static void encode_base64(u_int8_t *, u_int8_t *, u_int16_t);
80
+ static void decode_base64(u_int8_t *, u_int16_t, u_int8_t *);
81
+
82
+ static char encrypted[_PASSWORD_LEN];
83
+ static char gsalt[7 + (BCRYPT_MAXSALT * 4 + 2) / 3 + 1];
84
+ static char error[] = ":";
85
+
86
+ const static u_int8_t Base64Code[] =
87
+ "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
88
+
89
+ const static u_int8_t index_64[128] = {
90
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
91
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
92
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
93
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
94
+ 255, 255, 255, 255, 255, 255, 0, 1, 54, 55,
95
+ 56, 57, 58, 59, 60, 61, 62, 63, 255, 255,
96
+ 255, 255, 255, 255, 255, 2, 3, 4, 5, 6,
97
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
98
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
99
+ 255, 255, 255, 255, 255, 255, 28, 29, 30,
100
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
101
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
102
+ 51, 52, 53, 255, 255, 255, 255, 255
103
+ };
104
+ #define CHAR64(c) ( (c) > 127 ? 255 : index_64[(c)])
105
+
106
+ static void
107
+ decode_base64(u_int8_t *buffer, u_int16_t len, u_int8_t *data)
108
+ {
109
+ u_int8_t *bp = buffer;
110
+ u_int8_t *p = data;
111
+ u_int8_t c1, c2, c3, c4;
112
+ while (bp < buffer + len) {
113
+ c1 = CHAR64(*p);
114
+ c2 = CHAR64(*(p + 1));
115
+
116
+ /* Invalid data */
117
+ if (c1 == 255 || c2 == 255)
118
+ break;
119
+
120
+ *bp++ = (c1 << 2) | ((c2 & 0x30) >> 4);
121
+ if (bp >= buffer + len)
122
+ break;
123
+
124
+ c3 = CHAR64(*(p + 2));
125
+ if (c3 == 255)
126
+ break;
127
+
128
+ *bp++ = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2);
129
+ if (bp >= buffer + len)
130
+ break;
131
+
132
+ c4 = CHAR64(*(p + 3));
133
+ if (c4 == 255)
134
+ break;
135
+ *bp++ = ((c3 & 0x03) << 6) | c4;
136
+
137
+ p += 4;
138
+ }
139
+ }
140
+
141
+ static void
142
+ encode_salt(char *salt, u_int8_t *csalt, u_int16_t clen, u_int8_t logr)
143
+ {
144
+ salt[0] = '$';
145
+ salt[1] = BCRYPT_VERSION;
146
+ salt[2] = 'a';
147
+ salt[3] = '$';
148
+
149
+ snprintf(salt + 4, 4, "%2.2u$", logr);
150
+
151
+ encode_base64((u_int8_t *) salt + 7, csalt, clen);
152
+ }
153
+ /* Generates a salt for this version of crypt.
154
+ Since versions may change. Keeping this here
155
+ seems sensible.
156
+ */
157
+
158
+ char *
159
+ bcrypt_gensalt(u_int8_t log_rounds, u_int8_t *rseed)
160
+ {
161
+ if (log_rounds < 4)
162
+ log_rounds = 4;
163
+ else if (log_rounds > 31)
164
+ log_rounds = 31;
165
+
166
+ encode_salt(gsalt, rseed, BCRYPT_MAXSALT, log_rounds);
167
+ return gsalt;
168
+ }
169
+ /* We handle $Vers$log2(NumRounds)$salt+passwd$
170
+ i.e. $2$04$iwouldntknowwhattosayetKdJ6iFtacBqJdKe6aW7ou */
171
+
172
+ char *
173
+ bcrypt(const char *key, const char *salt)
174
+ {
175
+ blf_ctx state;
176
+ u_int32_t rounds, i, k;
177
+ u_int16_t j;
178
+ u_int8_t key_len, salt_len, logr, minor;
179
+ u_int8_t ciphertext[4 * BCRYPT_BLOCKS] = "OrpheanBeholderScryDoubt";
180
+ u_int8_t csalt[BCRYPT_MAXSALT];
181
+ u_int32_t cdata[BCRYPT_BLOCKS];
182
+ int n;
183
+
184
+ /* Discard "$" identifier */
185
+ salt++;
186
+
187
+ if (*salt > BCRYPT_VERSION) {
188
+ /* How do I handle errors ? Return ':' */
189
+ return error;
190
+ }
191
+
192
+ /* Check for minor versions */
193
+ if (salt[1] != '$') {
194
+ switch (salt[1]) {
195
+ case 'a':
196
+ /* 'ab' should not yield the same as 'abab' */
197
+ minor = salt[1];
198
+ salt++;
199
+ break;
200
+ default:
201
+ return error;
202
+ }
203
+ } else
204
+ minor = 0;
205
+
206
+ /* Discard version + "$" identifier */
207
+ salt += 2;
208
+
209
+ if (salt[2] != '$')
210
+ /* Out of sync with passwd entry */
211
+ return error;
212
+
213
+ /* Computer power doesn't increase linear, 2^x should be fine */
214
+ n = atoi(salt);
215
+ if (n > 31 || n < 0)
216
+ return error;
217
+ logr = (u_int8_t)n;
218
+ if ((rounds = (u_int32_t) 1 << logr) < BCRYPT_MINROUNDS)
219
+ return error;
220
+
221
+ /* Discard num rounds + "$" identifier */
222
+ salt += 3;
223
+
224
+ if (strlen(salt) * 3 / 4 < BCRYPT_MAXSALT)
225
+ return error;
226
+
227
+ /* We dont want the base64 salt but the raw data */
228
+ decode_base64(csalt, BCRYPT_MAXSALT, (u_int8_t *) salt);
229
+ salt_len = BCRYPT_MAXSALT;
230
+ key_len = strlen(key) + (minor >= 'a' ? 1 : 0);
231
+
232
+ /* Setting up S-Boxes and Subkeys */
233
+ Blowfish_initstate(&state);
234
+ Blowfish_expandstate(&state, csalt, salt_len,
235
+ (u_int8_t *) key, key_len);
236
+ for (k = 0; k < rounds; k++) {
237
+ Blowfish_expand0state(&state, (u_int8_t *) key, key_len);
238
+ Blowfish_expand0state(&state, csalt, salt_len);
239
+ }
240
+
241
+ /* This can be precomputed later */
242
+ j = 0;
243
+ for (i = 0; i < BCRYPT_BLOCKS; i++)
244
+ cdata[i] = Blowfish_stream2word(ciphertext, 4 * BCRYPT_BLOCKS, &j);
245
+
246
+ /* Now do the encryption */
247
+ for (k = 0; k < 64; k++)
248
+ blf_enc(&state, cdata, BCRYPT_BLOCKS / 2);
249
+
250
+ for (i = 0; i < BCRYPT_BLOCKS; i++) {
251
+ ciphertext[4 * i + 3] = cdata[i] & 0xff;
252
+ cdata[i] = cdata[i] >> 8;
253
+ ciphertext[4 * i + 2] = cdata[i] & 0xff;
254
+ cdata[i] = cdata[i] >> 8;
255
+ ciphertext[4 * i + 1] = cdata[i] & 0xff;
256
+ cdata[i] = cdata[i] >> 8;
257
+ ciphertext[4 * i + 0] = cdata[i] & 0xff;
258
+ }
259
+
260
+
261
+ i = 0;
262
+ encrypted[i++] = '$';
263
+ encrypted[i++] = BCRYPT_VERSION;
264
+ if (minor)
265
+ encrypted[i++] = minor;
266
+ encrypted[i++] = '$';
267
+
268
+ snprintf(encrypted + i, 4, "%2.2u$", logr);
269
+
270
+ encode_base64((u_int8_t *) encrypted + i + 3, csalt, BCRYPT_MAXSALT);
271
+ encode_base64((u_int8_t *) encrypted + strlen(encrypted), ciphertext,
272
+ 4 * BCRYPT_BLOCKS - 1);
273
+ return encrypted;
274
+ }
275
+
276
+ static void
277
+ encode_base64(u_int8_t *buffer, u_int8_t *data, u_int16_t len)
278
+ {
279
+ u_int8_t *bp = buffer;
280
+ u_int8_t *p = data;
281
+ u_int8_t c1, c2;
282
+ while (p < data + len) {
283
+ c1 = *p++;
284
+ *bp++ = Base64Code[(c1 >> 2)];
285
+ c1 = (c1 & 0x03) << 4;
286
+ if (p >= data + len) {
287
+ *bp++ = Base64Code[c1];
288
+ break;
289
+ }
290
+ c2 = *p++;
291
+ c1 |= (c2 >> 4) & 0x0f;
292
+ *bp++ = Base64Code[c1];
293
+ c1 = (c2 & 0x0f) << 2;
294
+ if (p >= data + len) {
295
+ *bp++ = Base64Code[c1];
296
+ break;
297
+ }
298
+ c2 = *p++;
299
+ c1 |= (c2 >> 6) & 0x03;
300
+ *bp++ = Base64Code[c1];
301
+ *bp++ = Base64Code[c2 & 0x3f];
302
+ }
303
+ *bp = '\0';
304
+ }
305
+ #if 0
306
+ void
307
+ main()
308
+ {
309
+ char blubber[73];
310
+ char salt[100];
311
+ char *p;
312
+ salt[0] = '$';
313
+ salt[1] = BCRYPT_VERSION;
314
+ salt[2] = '$';
315
+
316
+ snprintf(salt + 3, 4, "%2.2u$", 5);
317
+
318
+ printf("24 bytes of salt: ");
319
+ fgets(salt + 6, sizeof(salt) - 6, stdin);
320
+ salt[99] = 0;
321
+ printf("72 bytes of password: ");
322
+ fpurge(stdin);
323
+ fgets(blubber, sizeof(blubber), stdin);
324
+ blubber[72] = 0;
325
+
326
+ p = crypt(blubber, salt);
327
+ printf("Passwd entry: %s\n\n", p);
328
+
329
+ p = bcrypt_gensalt(5);
330
+ printf("Generated salt: %s\n", p);
331
+ p = crypt(blubber, p);
332
+ printf("Passwd entry: %s\n", p);
333
+ }
334
+ #endif