bcrypt-ruby 2.1.4-x86-mingw32 → 3.0.0-x86-mingw32
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.
- data/.gitignore +1 -0
- data/CHANGELOG +5 -1
- data/COPYING +23 -28
- data/Gemfile.lock +29 -0
- data/README.md +184 -0
- data/Rakefile +1 -0
- data/bcrypt-ruby.gemspec +3 -3
- data/ext/mri/bcrypt_ext.c +67 -65
- data/ext/mri/crypt.c +57 -0
- data/ext/mri/crypt.h +13 -0
- data/ext/mri/{blowfish.c → crypt_blowfish.c} +472 -321
- data/ext/mri/crypt_gensalt.c +111 -0
- data/ext/mri/extconf.rb +24 -2
- data/ext/mri/ow-crypt.h +35 -0
- data/ext/mri/wrapper.c +255 -0
- data/lib/bcrypt.rb +10 -5
- data/lib/bcrypt_engine.rb +34 -0
- data/spec/bcrypt/engine_spec.rb +3 -3
- data/spec/bcrypt/password_spec.rb +11 -2
- metadata +19 -16
- data/README +0 -175
- data/ext/mri/bcrypt.c +0 -297
- data/ext/mri/bcrypt.h +0 -67
- data/ext/mri/blf.h +0 -86
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module BCrypt
|
4
|
+
class Engine
|
5
|
+
extend FFI::Library
|
6
|
+
|
7
|
+
BCRYPT_MAXSALT = 16
|
8
|
+
BCRYPT_SALT_OUTPUT_SIZE = 7 + (BCRYPT_MAXSALT * 4 + 2) / 3 + 1
|
9
|
+
BCRYPT_OUTPUT_SIZE = 128
|
10
|
+
|
11
|
+
ffi_lib File.expand_path("../bcrypt_ext", __FILE__)
|
12
|
+
|
13
|
+
attach_function :ruby_bcrypt, [:buffer_out, :string, :string], :string
|
14
|
+
attach_function :ruby_bcrypt_gensalt, [:buffer_out, :uint8, :pointer], :string
|
15
|
+
|
16
|
+
def self.__bc_salt(cost, seed)
|
17
|
+
buffer_out = FFI::Buffer.alloc_out(BCRYPT_SALT_OUTPUT_SIZE, 1)
|
18
|
+
seed_ptr = FFI::MemoryPointer.new(:uint8, BCRYPT_MAXSALT)
|
19
|
+
seed.bytes.to_a.each_with_index { |b, i| seed_ptr.int8_put(i, b) }
|
20
|
+
out = ruby_bcrypt_gensalt(buffer_out, cost, seed_ptr)
|
21
|
+
seed_ptr.free
|
22
|
+
buffer_out.free
|
23
|
+
out || ""
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.__bc_crypt(key, salt, cost)
|
27
|
+
buffer_out = FFI::Buffer.alloc_out(BCRYPT_OUTPUT_SIZE, 1)
|
28
|
+
out = ruby_bcrypt(buffer_out, key || "", salt)
|
29
|
+
buffer_out.free
|
30
|
+
out && out.any? ? out : nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
data/spec/bcrypt/engine_spec.rb
CHANGED
@@ -30,9 +30,9 @@ end
|
|
30
30
|
describe "Autodetecting of salt cost" do
|
31
31
|
|
32
32
|
specify "should work" do
|
33
|
-
BCrypt::Engine.autodetect_cost("$2a$08$hRx2IVeHNsTSYYtUWn61Ou").should
|
34
|
-
BCrypt::Engine.autodetect_cost("$2a$05$XKd1bMnLgUnc87qvbAaCUu").should
|
35
|
-
BCrypt::Engine.autodetect_cost("$2a$13$Lni.CZ6z5A7344POTFBBV.").should
|
33
|
+
BCrypt::Engine.autodetect_cost("$2a$08$hRx2IVeHNsTSYYtUWn61Ou").should eq 8
|
34
|
+
BCrypt::Engine.autodetect_cost("$2a$05$XKd1bMnLgUnc87qvbAaCUu").should eq 5
|
35
|
+
BCrypt::Engine.autodetect_cost("$2a$13$Lni.CZ6z5A7344POTFBBV.").should eq 13
|
36
36
|
end
|
37
37
|
|
38
38
|
end
|
@@ -39,9 +39,9 @@ describe "Reading a hashed password" do
|
|
39
39
|
password.version.should eql("2a")
|
40
40
|
password.cost.should equal(5)
|
41
41
|
password.salt.should eql("$2a$05$CCCCCCCCCCCCCCCCCCCCC.")
|
42
|
-
password.salt.class.should
|
42
|
+
password.salt.class.should eq String
|
43
43
|
password.checksum.should eq("E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW")
|
44
|
-
password.checksum.class.should
|
44
|
+
password.checksum.class.should eq String
|
45
45
|
password.to_s.should eql(@hash)
|
46
46
|
end
|
47
47
|
|
@@ -65,3 +65,12 @@ describe "Comparing a hashed password with a secret" do
|
|
65
65
|
(@password == "@secret").should be(false)
|
66
66
|
end
|
67
67
|
end
|
68
|
+
|
69
|
+
describe "Validating a generated salt" do
|
70
|
+
specify "should not accept an invalid salt" do
|
71
|
+
BCrypt::Engine.valid_salt?("invalid").should eq(false)
|
72
|
+
end
|
73
|
+
specify "should accept a valid salt" do
|
74
|
+
BCrypt::Engine.valid_salt?(BCrypt::Engine.generate_salt).should eq(true)
|
75
|
+
end
|
76
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bcrypt-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 7
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
|
-
-
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version:
|
7
|
+
- 3
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 3.0.0
|
11
11
|
platform: x86-mingw32
|
12
12
|
authors:
|
13
13
|
- Coda Hale
|
@@ -15,8 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
19
|
-
default_executable:
|
18
|
+
date: 2011-08-24 00:00:00 Z
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
22
21
|
name: rake-compiler
|
@@ -53,27 +52,32 @@ executables: []
|
|
53
52
|
extensions: []
|
54
53
|
|
55
54
|
extra_rdoc_files:
|
56
|
-
- README
|
55
|
+
- README.md
|
57
56
|
- COPYING
|
58
57
|
- CHANGELOG
|
59
58
|
- lib/bcrypt.rb
|
59
|
+
- lib/bcrypt_engine.rb
|
60
60
|
files:
|
61
61
|
- .gitignore
|
62
62
|
- .rspec
|
63
63
|
- CHANGELOG
|
64
64
|
- COPYING
|
65
65
|
- Gemfile
|
66
|
-
-
|
66
|
+
- Gemfile.lock
|
67
|
+
- README.md
|
67
68
|
- Rakefile
|
68
69
|
- bcrypt-ruby.gemspec
|
69
70
|
- ext/jruby/bcrypt_jruby/BCrypt.java
|
70
|
-
- ext/mri/bcrypt.c
|
71
|
-
- ext/mri/bcrypt.h
|
72
71
|
- ext/mri/bcrypt_ext.c
|
73
|
-
- ext/mri/
|
74
|
-
- ext/mri/
|
72
|
+
- ext/mri/crypt.c
|
73
|
+
- ext/mri/crypt.h
|
74
|
+
- ext/mri/crypt_blowfish.c
|
75
|
+
- ext/mri/crypt_gensalt.c
|
75
76
|
- ext/mri/extconf.rb
|
77
|
+
- ext/mri/ow-crypt.h
|
78
|
+
- ext/mri/wrapper.c
|
76
79
|
- lib/bcrypt.rb
|
80
|
+
- lib/bcrypt_engine.rb
|
77
81
|
- spec/TestBCrypt.java
|
78
82
|
- spec/bcrypt/engine_spec.rb
|
79
83
|
- spec/bcrypt/password_spec.rb
|
@@ -81,7 +85,6 @@ files:
|
|
81
85
|
- lib/1.8/bcrypt_ext.so
|
82
86
|
- lib/1.9/bcrypt_ext.so
|
83
87
|
- lib/bcrypt_ext.rb
|
84
|
-
has_rdoc: true
|
85
88
|
homepage: http://bcrypt-ruby.rubyforge.org
|
86
89
|
licenses: []
|
87
90
|
|
@@ -92,7 +95,7 @@ rdoc_options:
|
|
92
95
|
- --line-numbers
|
93
96
|
- --inline-source
|
94
97
|
- --main
|
95
|
-
- README
|
98
|
+
- README.md
|
96
99
|
require_paths:
|
97
100
|
- lib
|
98
101
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -116,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
119
|
requirements: []
|
117
120
|
|
118
121
|
rubyforge_project: bcrypt-ruby
|
119
|
-
rubygems_version: 1.
|
122
|
+
rubygems_version: 1.8.8
|
120
123
|
signing_key:
|
121
124
|
specification_version: 3
|
122
125
|
summary: OpenBSD's bcrypt() password hashing algorithm.
|
data/README
DELETED
@@ -1,175 +0,0 @@
|
|
1
|
-
= bcrypt-ruby
|
2
|
-
|
3
|
-
An easy way to keep your users' passwords secure.
|
4
|
-
|
5
|
-
* http://bcrypt-ruby.rubyforge.org/
|
6
|
-
* http://github.com/codahale/bcrypt-ruby/tree/master
|
7
|
-
|
8
|
-
== Why you should use bcrypt
|
9
|
-
|
10
|
-
If you store user passwords in the clear, then an attacker who steals a copy of your database has a giant list of emails
|
11
|
-
and passwords. Some of your users will only have one password -- for their email account, for their banking account, for
|
12
|
-
your application. A simple hack could escalate into massive identity theft.
|
13
|
-
|
14
|
-
It's your responsibility as a web developer to make your web application secure -- blaming your users for not being
|
15
|
-
security experts is not a professional response to risk.
|
16
|
-
|
17
|
-
bcrypt allows you to easily harden your application against these kinds of attacks.
|
18
|
-
|
19
|
-
== How to install bcrypt
|
20
|
-
|
21
|
-
sudo gem install bcrypt-ruby
|
22
|
-
|
23
|
-
You'll need a working compiler. (Win32 folks should use Cygwin or um, something else.)
|
24
|
-
|
25
|
-
== How to use bcrypt in your Rails application
|
26
|
-
|
27
|
-
=== The +User+ model
|
28
|
-
|
29
|
-
require 'bcrypt'
|
30
|
-
|
31
|
-
class User < ActiveRecord::Base
|
32
|
-
# users.password_hash in the database is a :string
|
33
|
-
include BCrypt
|
34
|
-
|
35
|
-
def password
|
36
|
-
@password ||= Password.new(password_hash)
|
37
|
-
end
|
38
|
-
|
39
|
-
def password=(new_password)
|
40
|
-
@password = Password.create(new_password)
|
41
|
-
self.password_hash = @password
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
45
|
-
|
46
|
-
=== Creating an account
|
47
|
-
|
48
|
-
def create
|
49
|
-
@user = User.new(params[:user])
|
50
|
-
@user.password = params[:password]
|
51
|
-
@user.save!
|
52
|
-
end
|
53
|
-
|
54
|
-
=== Authenticating a user
|
55
|
-
|
56
|
-
def login
|
57
|
-
@user = User.find_by_email(params[:email])
|
58
|
-
if @user.password == params[:password]
|
59
|
-
give_token
|
60
|
-
else
|
61
|
-
redirect_to home_url
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
=== If a user forgets their password?
|
66
|
-
|
67
|
-
# assign them a random one and mail it to them, asking them to change it
|
68
|
-
def forgot_password
|
69
|
-
@user = User.find_by_email(params[:email])
|
70
|
-
random_password = Array.new(10).map { (65 + rand(58)).chr }.join
|
71
|
-
@user.password = random_password
|
72
|
-
@user.save!
|
73
|
-
Mailer.create_and_deliver_password_change(@user, random_password)
|
74
|
-
end
|
75
|
-
|
76
|
-
== How to use bcrypt-ruby in general
|
77
|
-
|
78
|
-
require 'bcrypt'
|
79
|
-
|
80
|
-
my_password = BCrypt::Password.create("my password") #=> "$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa"
|
81
|
-
|
82
|
-
my_password.version #=> "2a"
|
83
|
-
my_password.cost #=> 10
|
84
|
-
my_password == "my password" #=> true
|
85
|
-
my_password == "not my password" #=> false
|
86
|
-
|
87
|
-
my_password = BCrypt::Password.new("$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa")
|
88
|
-
my_password == "my password" #=> true
|
89
|
-
my_password == "not my password" #=> false
|
90
|
-
|
91
|
-
Check the rdocs for more details -- BCrypt, BCrypt::Password.
|
92
|
-
|
93
|
-
== How bcrypt() works
|
94
|
-
|
95
|
-
bcrypt() is a hashing algorithm designed by Niels Provos and David Mazières of the OpenBSD Project.
|
96
|
-
|
97
|
-
=== Background
|
98
|
-
|
99
|
-
Hash algorithms take a chunk of data (e.g., your user's password) and create a "digital fingerprint," or hash, of it.
|
100
|
-
Because this process is not reversible, there's no way to go from the hash back to the password.
|
101
|
-
|
102
|
-
In other words:
|
103
|
-
|
104
|
-
hash(p) #=> <unique gibberish>
|
105
|
-
|
106
|
-
You can store the hash and check it against a hash made of a potentially valid password:
|
107
|
-
|
108
|
-
<unique gibberish> =? hash(just_entered_password)
|
109
|
-
|
110
|
-
=== Rainbow Tables
|
111
|
-
|
112
|
-
But even this has weaknesses -- attackers can just run lists of possible passwords through the same algorithm, store the
|
113
|
-
results in a big database, and then look up the passwords by their hash:
|
114
|
-
|
115
|
-
PrecomputedPassword.find_by_hash(<unique gibberish>).password #=> "secret1"
|
116
|
-
|
117
|
-
=== Salts
|
118
|
-
|
119
|
-
The solution to this is to add a small chunk of random data -- called a salt -- to the password before it's hashed:
|
120
|
-
|
121
|
-
hash(salt + p) #=> <really unique gibberish>
|
122
|
-
|
123
|
-
The salt is then stored along with the hash in the database, and used to check potentially valid passwords:
|
124
|
-
|
125
|
-
<really unique gibberish> =? hash(salt + just_entered_password)
|
126
|
-
|
127
|
-
bcrypt-ruby automatically handles the storage and generation of these salts for you.
|
128
|
-
|
129
|
-
Adding a salt means that an attacker has to have a gigantic database for each unique salt -- for a salt made of 4
|
130
|
-
letters, that's 456,976 different databases. Pretty much no one has that much storage space, so attackers try a
|
131
|
-
different, slower method -- throw a list of potential passwords at each individual password:
|
132
|
-
|
133
|
-
hash(salt + "aadvark") =? <really unique gibberish>
|
134
|
-
hash(salt + "abacus") =? <really unique gibberish>
|
135
|
-
etc.
|
136
|
-
|
137
|
-
This is much slower than the big database approach, but most hash algorithms are pretty quick -- and therein lies the
|
138
|
-
problem. Hash algorithms aren't usually designed to be slow, they're designed to turn gigabytes of data into secure
|
139
|
-
fingerprints as quickly as possible. bcrypt(), though, is designed to be computationally expensive:
|
140
|
-
|
141
|
-
Ten thousand iterations:
|
142
|
-
user system total real
|
143
|
-
md5 0.070000 0.000000 0.070000 ( 0.070415)
|
144
|
-
bcrypt 22.230000 0.080000 22.310000 ( 22.493822)
|
145
|
-
|
146
|
-
If an attacker was using Ruby to check each password, they could check ~140,000 passwords a second with MD5 but only
|
147
|
-
~450 passwords a second with bcrypt().
|
148
|
-
|
149
|
-
=== Cost Factors
|
150
|
-
|
151
|
-
In addition, bcrypt() allows you to increase the amount of work required to hash a password as computers get faster. Old
|
152
|
-
passwords will still work fine, but new passwords can keep up with the times.
|
153
|
-
|
154
|
-
The default cost factor used by bcrypt-ruby is 10, which is fine for session-based authentication. If you are using a
|
155
|
-
stateless authentication architecture (e.g., HTTP Basic Auth), you will want to lower the cost factor to reduce your
|
156
|
-
server load and keep your request times down. This will lower the security provided you, but there are few alternatives.
|
157
|
-
|
158
|
-
== More Information
|
159
|
-
|
160
|
-
bcrypt() is currently used as the default password storage hash in OpenBSD, widely regarded as the most secure operating
|
161
|
-
system available.
|
162
|
-
|
163
|
-
|
164
|
-
For a more technical explanation of the algorithm and its design criteria, please read Niels Provos and David Mazières'
|
165
|
-
Usenix99 paper:
|
166
|
-
http://www.usenix.org/events/usenix99/provos.html
|
167
|
-
|
168
|
-
If you'd like more down-to-earth advice regarding cryptography, I suggest reading <i>Practical Cryptography</i> by Niels
|
169
|
-
Ferguson and Bruce Schneier:
|
170
|
-
http://www.schneier.com/book-practical.html
|
171
|
-
|
172
|
-
= Etc
|
173
|
-
|
174
|
-
Author :: Coda Hale <coda.hale@gmail.com>
|
175
|
-
Website :: http://blog.codahale.com
|
data/ext/mri/bcrypt.c
DELETED
@@ -1,297 +0,0 @@
|
|
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 2009-09-16:
|
5
|
-
*
|
6
|
-
* - Standardized on stdint.h's numerical types and removed some debug cruft.
|
7
|
-
*
|
8
|
-
* Modified by <hongli@phusion.nl> on 2009-08-05:
|
9
|
-
*
|
10
|
-
* - Got rid of the global variables; they're not thread-safe.
|
11
|
-
* Modified the functions to accept local buffers instead.
|
12
|
-
*
|
13
|
-
* Modified by <coda.hale@gmail.com> on 2007-02-27:
|
14
|
-
*
|
15
|
-
* - Changed bcrypt_gensalt to accept a random seed as a parameter,
|
16
|
-
* to remove the code's dependency on arc4random(), which isn't
|
17
|
-
* available on Linux.
|
18
|
-
*/
|
19
|
-
|
20
|
-
/*
|
21
|
-
* Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
|
22
|
-
* All rights reserved.
|
23
|
-
*
|
24
|
-
* Redistribution and use in source and binary forms, with or without
|
25
|
-
* modification, are permitted provided that the following conditions
|
26
|
-
* are met:
|
27
|
-
* 1. Redistributions of source code must retain the above copyright
|
28
|
-
* notice, this list of conditions and the following disclaimer.
|
29
|
-
* 2. Redistributions in binary form must reproduce the above copyright
|
30
|
-
* notice, this list of conditions and the following disclaimer in the
|
31
|
-
* documentation and/or other materials provided with the distribution.
|
32
|
-
* 3. All advertising materials mentioning features or use of this software
|
33
|
-
* must display the following acknowledgement:
|
34
|
-
* This product includes software developed by Niels Provos.
|
35
|
-
* 4. The name of the author may not be used to endorse or promote products
|
36
|
-
* derived from this software without specific prior written permission.
|
37
|
-
*
|
38
|
-
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
39
|
-
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
40
|
-
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
41
|
-
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
42
|
-
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
43
|
-
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
44
|
-
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
45
|
-
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
46
|
-
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
47
|
-
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
48
|
-
*/
|
49
|
-
|
50
|
-
/* This password hashing algorithm was designed by David Mazieres
|
51
|
-
* <dm@lcs.mit.edu> and works as follows:
|
52
|
-
*
|
53
|
-
* 1. state := InitState ()
|
54
|
-
* 2. state := ExpandKey (state, salt, password) 3.
|
55
|
-
* REPEAT rounds:
|
56
|
-
* state := ExpandKey (state, 0, salt)
|
57
|
-
* state := ExpandKey(state, 0, password)
|
58
|
-
* 4. ctext := "OrpheanBeholderScryDoubt"
|
59
|
-
* 5. REPEAT 64:
|
60
|
-
* ctext := Encrypt_ECB (state, ctext);
|
61
|
-
* 6. RETURN Concatenate (salt, ctext);
|
62
|
-
*
|
63
|
-
*/
|
64
|
-
|
65
|
-
#include <stdio.h>
|
66
|
-
#include <stdlib.h>
|
67
|
-
#include <string.h>
|
68
|
-
#include "blf.h"
|
69
|
-
#include "bcrypt.h"
|
70
|
-
|
71
|
-
/* This implementation is adaptable to current computing power.
|
72
|
-
* You can have up to 2^31 rounds which should be enough for some
|
73
|
-
* time to come.
|
74
|
-
*/
|
75
|
-
|
76
|
-
static void encode_salt(char *, uint8_t *, uint16_t, uint8_t);
|
77
|
-
static void encode_base64(uint8_t *, uint8_t *, uint16_t);
|
78
|
-
static void decode_base64(uint8_t *, uint16_t, uint8_t *);
|
79
|
-
|
80
|
-
static const uint8_t Base64Code[] =
|
81
|
-
"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
82
|
-
|
83
|
-
static const uint8_t index_64[128] = {
|
84
|
-
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
85
|
-
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
86
|
-
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
87
|
-
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
88
|
-
255, 255, 255, 255, 255, 255, 0, 1, 54, 55,
|
89
|
-
56, 57, 58, 59, 60, 61, 62, 63, 255, 255,
|
90
|
-
255, 255, 255, 255, 255, 2, 3, 4, 5, 6,
|
91
|
-
7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
92
|
-
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
|
93
|
-
255, 255, 255, 255, 255, 255, 28, 29, 30,
|
94
|
-
31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
95
|
-
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
|
96
|
-
51, 52, 53, 255, 255, 255, 255, 255
|
97
|
-
};
|
98
|
-
#define CHAR64(c) ( (c) > 127 ? 255 : index_64[(c)])
|
99
|
-
|
100
|
-
static void
|
101
|
-
decode_base64(uint8_t *buffer, uint16_t len, uint8_t *data)
|
102
|
-
{
|
103
|
-
uint8_t *bp = buffer;
|
104
|
-
uint8_t *p = data;
|
105
|
-
uint8_t c1, c2, c3, c4;
|
106
|
-
while (bp < buffer + len) {
|
107
|
-
c1 = CHAR64(*p);
|
108
|
-
c2 = CHAR64(*(p + 1));
|
109
|
-
|
110
|
-
/* Invalid data */
|
111
|
-
if (c1 == 255 || c2 == 255)
|
112
|
-
break;
|
113
|
-
|
114
|
-
*bp++ = (c1 << 2) | ((c2 & 0x30) >> 4);
|
115
|
-
if (bp >= buffer + len)
|
116
|
-
break;
|
117
|
-
|
118
|
-
c3 = CHAR64(*(p + 2));
|
119
|
-
if (c3 == 255)
|
120
|
-
break;
|
121
|
-
|
122
|
-
*bp++ = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2);
|
123
|
-
if (bp >= buffer + len)
|
124
|
-
break;
|
125
|
-
|
126
|
-
c4 = CHAR64(*(p + 3));
|
127
|
-
if (c4 == 255)
|
128
|
-
break;
|
129
|
-
*bp++ = ((c3 & 0x03) << 6) | c4;
|
130
|
-
|
131
|
-
p += 4;
|
132
|
-
}
|
133
|
-
}
|
134
|
-
|
135
|
-
static void
|
136
|
-
encode_salt(char *salt, uint8_t *csalt, uint16_t clen, uint8_t logr)
|
137
|
-
{
|
138
|
-
salt[0] = '$';
|
139
|
-
salt[1] = BCRYPT_VERSION;
|
140
|
-
salt[2] = 'a';
|
141
|
-
salt[3] = '$';
|
142
|
-
|
143
|
-
snprintf(salt + 4, 4, "%2.2u$", logr);
|
144
|
-
|
145
|
-
encode_base64((uint8_t *) salt + 7, csalt, clen);
|
146
|
-
}
|
147
|
-
/* Generates a salt for this version of crypt.
|
148
|
-
Since versions may change. Keeping this here
|
149
|
-
seems sensible.
|
150
|
-
*/
|
151
|
-
|
152
|
-
char *
|
153
|
-
ruby_bcrypt_gensalt(char *output, uint8_t log_rounds, uint8_t *rseed)
|
154
|
-
{
|
155
|
-
if (log_rounds < 4)
|
156
|
-
log_rounds = 4;
|
157
|
-
else if (log_rounds > 31)
|
158
|
-
log_rounds = 31;
|
159
|
-
|
160
|
-
encode_salt(output, rseed, BCRYPT_MAXSALT, log_rounds);
|
161
|
-
return output;
|
162
|
-
}
|
163
|
-
/* We handle $Vers$log2(NumRounds)$salt+passwd$
|
164
|
-
i.e. $2$04$iwouldntknowwhattosayetKdJ6iFtacBqJdKe6aW7ou */
|
165
|
-
|
166
|
-
char *
|
167
|
-
ruby_bcrypt(char *output, const char *key, const char *salt)
|
168
|
-
{
|
169
|
-
blf_ctx state;
|
170
|
-
uint32_t rounds, i, k;
|
171
|
-
uint16_t j;
|
172
|
-
uint8_t key_len, salt_len, logr, minor;
|
173
|
-
uint8_t ciphertext[4 * BCRYPT_BLOCKS] = "OrpheanBeholderScryDoubt";
|
174
|
-
uint8_t csalt[BCRYPT_MAXSALT];
|
175
|
-
uint32_t cdata[BCRYPT_BLOCKS];
|
176
|
-
int n;
|
177
|
-
|
178
|
-
/* Discard "$" identifier */
|
179
|
-
salt++;
|
180
|
-
|
181
|
-
if (*salt > BCRYPT_VERSION) {
|
182
|
-
return NULL;
|
183
|
-
}
|
184
|
-
|
185
|
-
/* Check for minor versions */
|
186
|
-
if (salt[1] != '$') {
|
187
|
-
switch (salt[1]) {
|
188
|
-
case 'a':
|
189
|
-
/* 'ab' should not yield the same as 'abab' */
|
190
|
-
minor = salt[1];
|
191
|
-
salt++;
|
192
|
-
break;
|
193
|
-
default:
|
194
|
-
return NULL;
|
195
|
-
}
|
196
|
-
} else
|
197
|
-
minor = 0;
|
198
|
-
|
199
|
-
/* Discard version + "$" identifier */
|
200
|
-
salt += 2;
|
201
|
-
|
202
|
-
if (salt[2] != '$')
|
203
|
-
/* Out of sync with passwd entry */
|
204
|
-
return NULL;
|
205
|
-
|
206
|
-
/* Computer power doesn't increase linear, 2^x should be fine */
|
207
|
-
n = atoi(salt);
|
208
|
-
if (n > 31 || n < 0)
|
209
|
-
return NULL;
|
210
|
-
logr = (uint8_t)n;
|
211
|
-
if ((rounds = (uint32_t) 1 << logr) < BCRYPT_MINROUNDS)
|
212
|
-
return NULL;
|
213
|
-
|
214
|
-
/* Discard num rounds + "$" identifier */
|
215
|
-
salt += 3;
|
216
|
-
|
217
|
-
if (strlen(salt) * 3 / 4 < BCRYPT_MAXSALT)
|
218
|
-
return NULL;
|
219
|
-
|
220
|
-
/* We dont want the base64 salt but the raw data */
|
221
|
-
decode_base64(csalt, BCRYPT_MAXSALT, (uint8_t *) salt);
|
222
|
-
salt_len = BCRYPT_MAXSALT;
|
223
|
-
key_len = strlen(key) + (minor >= 'a' ? 1 : 0);
|
224
|
-
|
225
|
-
/* Setting up S-Boxes and Subkeys */
|
226
|
-
Blowfish_initstate(&state);
|
227
|
-
Blowfish_expandstate(&state, csalt, salt_len,
|
228
|
-
(uint8_t *) key, key_len);
|
229
|
-
for (k = 0; k < rounds; k++) {
|
230
|
-
Blowfish_expand0state(&state, (uint8_t *) key, key_len);
|
231
|
-
Blowfish_expand0state(&state, csalt, salt_len);
|
232
|
-
}
|
233
|
-
|
234
|
-
/* This can be precomputed later */
|
235
|
-
j = 0;
|
236
|
-
for (i = 0; i < BCRYPT_BLOCKS; i++)
|
237
|
-
cdata[i] = Blowfish_stream2word(ciphertext, 4 * BCRYPT_BLOCKS, &j);
|
238
|
-
|
239
|
-
/* Now do the encryption */
|
240
|
-
for (k = 0; k < 64; k++)
|
241
|
-
blf_enc(&state, cdata, BCRYPT_BLOCKS / 2);
|
242
|
-
|
243
|
-
for (i = 0; i < BCRYPT_BLOCKS; i++) {
|
244
|
-
ciphertext[4 * i + 3] = cdata[i] & 0xff;
|
245
|
-
cdata[i] = cdata[i] >> 8;
|
246
|
-
ciphertext[4 * i + 2] = cdata[i] & 0xff;
|
247
|
-
cdata[i] = cdata[i] >> 8;
|
248
|
-
ciphertext[4 * i + 1] = cdata[i] & 0xff;
|
249
|
-
cdata[i] = cdata[i] >> 8;
|
250
|
-
ciphertext[4 * i + 0] = cdata[i] & 0xff;
|
251
|
-
}
|
252
|
-
|
253
|
-
|
254
|
-
i = 0;
|
255
|
-
output[i++] = '$';
|
256
|
-
output[i++] = BCRYPT_VERSION;
|
257
|
-
if (minor)
|
258
|
-
output[i++] = minor;
|
259
|
-
output[i++] = '$';
|
260
|
-
|
261
|
-
snprintf(output + i, 4, "%2.2u$", logr);
|
262
|
-
|
263
|
-
encode_base64((uint8_t *) output + i + 3, csalt, BCRYPT_MAXSALT);
|
264
|
-
encode_base64((uint8_t *) output + strlen(output), ciphertext,
|
265
|
-
4 * BCRYPT_BLOCKS - 1);
|
266
|
-
return output;
|
267
|
-
}
|
268
|
-
|
269
|
-
static void
|
270
|
-
encode_base64(uint8_t *buffer, uint8_t *data, uint16_t len)
|
271
|
-
{
|
272
|
-
uint8_t *bp = buffer;
|
273
|
-
uint8_t *p = data;
|
274
|
-
uint8_t c1, c2;
|
275
|
-
while (p < data + len) {
|
276
|
-
c1 = *p++;
|
277
|
-
*bp++ = Base64Code[(c1 >> 2)];
|
278
|
-
c1 = (c1 & 0x03) << 4;
|
279
|
-
if (p >= data + len) {
|
280
|
-
*bp++ = Base64Code[c1];
|
281
|
-
break;
|
282
|
-
}
|
283
|
-
c2 = *p++;
|
284
|
-
c1 |= (c2 >> 4) & 0x0f;
|
285
|
-
*bp++ = Base64Code[c1];
|
286
|
-
c1 = (c2 & 0x0f) << 2;
|
287
|
-
if (p >= data + len) {
|
288
|
-
*bp++ = Base64Code[c1];
|
289
|
-
break;
|
290
|
-
}
|
291
|
-
c2 = *p++;
|
292
|
-
c1 |= (c2 >> 6) & 0x03;
|
293
|
-
*bp++ = Base64Code[c1];
|
294
|
-
*bp++ = Base64Code[c2 & 0x3f];
|
295
|
-
}
|
296
|
-
*bp = '\0';
|
297
|
-
}
|