bcrypt-ruby 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

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