bcrypt 3.1.14-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +3 -0
- data/.travis.yml +23 -0
- data/CHANGELOG +98 -0
- data/COPYING +28 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +37 -0
- data/README.md +194 -0
- data/Rakefile +72 -0
- data/appveyor.yml +50 -0
- data/bcrypt.gemspec +27 -0
- data/ext/jruby/bcrypt_jruby/BCrypt.java +925 -0
- data/ext/mri/bcrypt_ext.c +116 -0
- data/ext/mri/crypt.c +57 -0
- data/ext/mri/crypt.h +24 -0
- data/ext/mri/crypt_blowfish.c +903 -0
- data/ext/mri/crypt_blowfish.h +27 -0
- data/ext/mri/crypt_gensalt.c +124 -0
- data/ext/mri/crypt_gensalt.h +30 -0
- data/ext/mri/extconf.rb +22 -0
- data/ext/mri/ow-crypt.h +43 -0
- data/ext/mri/wrapper.c +554 -0
- data/ext/mri/x86.S +203 -0
- data/lib/bcrypt.rb +16 -0
- data/lib/bcrypt/engine.rb +118 -0
- data/lib/bcrypt/error.rb +22 -0
- data/lib/bcrypt/password.rb +87 -0
- data/spec/TestBCrypt.java +194 -0
- data/spec/bcrypt/engine_spec.rb +157 -0
- data/spec/bcrypt/error_spec.rb +37 -0
- data/spec/bcrypt/password_spec.rb +124 -0
- data/spec/spec_helper.rb +2 -0
- metadata +119 -0
data/ext/mri/x86.S
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
/*
|
2
|
+
* Written by Solar Designer <solar at openwall.com> in 1998-2010.
|
3
|
+
* No copyright is claimed, and the software is hereby placed in the public
|
4
|
+
* domain. In case this attempt to disclaim copyright and place the software
|
5
|
+
* in the public domain is deemed null and void, then the software is
|
6
|
+
* Copyright (c) 1998-2010 Solar Designer and it is hereby released to the
|
7
|
+
* general public under the following terms:
|
8
|
+
*
|
9
|
+
* Redistribution and use in source and binary forms, with or without
|
10
|
+
* modification, are permitted.
|
11
|
+
*
|
12
|
+
* There's ABSOLUTELY NO WARRANTY, express or implied.
|
13
|
+
*
|
14
|
+
* See crypt_blowfish.c for more information.
|
15
|
+
*/
|
16
|
+
|
17
|
+
#ifdef __i386__
|
18
|
+
|
19
|
+
#if defined(__OpenBSD__) && !defined(__ELF__)
|
20
|
+
#define UNDERSCORES
|
21
|
+
#define ALIGN_LOG
|
22
|
+
#endif
|
23
|
+
|
24
|
+
#if defined(__CYGWIN32__) || defined(__MINGW32__)
|
25
|
+
#define UNDERSCORES
|
26
|
+
#endif
|
27
|
+
|
28
|
+
#ifdef __DJGPP__
|
29
|
+
#define UNDERSCORES
|
30
|
+
#define ALIGN_LOG
|
31
|
+
#endif
|
32
|
+
|
33
|
+
#ifdef UNDERSCORES
|
34
|
+
#define _BF_body_r __BF_body_r
|
35
|
+
#endif
|
36
|
+
|
37
|
+
#ifdef ALIGN_LOG
|
38
|
+
#define DO_ALIGN(log) .align (log)
|
39
|
+
#elif defined(DUMBAS)
|
40
|
+
#define DO_ALIGN(log) .align 1 << log
|
41
|
+
#else
|
42
|
+
#define DO_ALIGN(log) .align (1 << (log))
|
43
|
+
#endif
|
44
|
+
|
45
|
+
#define BF_FRAME 0x200
|
46
|
+
#define ctx %esp
|
47
|
+
|
48
|
+
#define BF_ptr (ctx)
|
49
|
+
|
50
|
+
#define S(N, r) N+BF_FRAME(ctx,r,4)
|
51
|
+
#ifdef DUMBAS
|
52
|
+
#define P(N) 0x1000+N+N+N+N+BF_FRAME(ctx)
|
53
|
+
#else
|
54
|
+
#define P(N) 0x1000+4*N+BF_FRAME(ctx)
|
55
|
+
#endif
|
56
|
+
|
57
|
+
/*
|
58
|
+
* This version of the assembly code is optimized primarily for the original
|
59
|
+
* Intel Pentium but is also careful to avoid partial register stalls on the
|
60
|
+
* Pentium Pro family of processors (tested up to Pentium III Coppermine).
|
61
|
+
*
|
62
|
+
* It is possible to do 15% faster on the Pentium Pro family and probably on
|
63
|
+
* many non-Intel x86 processors, but, unfortunately, that would make things
|
64
|
+
* twice slower for the original Pentium.
|
65
|
+
*
|
66
|
+
* An additional 2% speedup may be achieved with non-reentrant code.
|
67
|
+
*/
|
68
|
+
|
69
|
+
#define L %esi
|
70
|
+
#define R %edi
|
71
|
+
#define tmp1 %eax
|
72
|
+
#define tmp1_lo %al
|
73
|
+
#define tmp2 %ecx
|
74
|
+
#define tmp2_hi %ch
|
75
|
+
#define tmp3 %edx
|
76
|
+
#define tmp3_lo %dl
|
77
|
+
#define tmp4 %ebx
|
78
|
+
#define tmp4_hi %bh
|
79
|
+
#define tmp5 %ebp
|
80
|
+
|
81
|
+
.text
|
82
|
+
|
83
|
+
#define BF_ROUND(L, R, N) \
|
84
|
+
xorl L,tmp2; \
|
85
|
+
xorl tmp1,tmp1; \
|
86
|
+
movl tmp2,L; \
|
87
|
+
shrl $16,tmp2; \
|
88
|
+
movl L,tmp4; \
|
89
|
+
movb tmp2_hi,tmp1_lo; \
|
90
|
+
andl $0xFF,tmp2; \
|
91
|
+
movb tmp4_hi,tmp3_lo; \
|
92
|
+
andl $0xFF,tmp4; \
|
93
|
+
movl S(0,tmp1),tmp1; \
|
94
|
+
movl S(0x400,tmp2),tmp5; \
|
95
|
+
addl tmp5,tmp1; \
|
96
|
+
movl S(0x800,tmp3),tmp5; \
|
97
|
+
xorl tmp5,tmp1; \
|
98
|
+
movl S(0xC00,tmp4),tmp5; \
|
99
|
+
addl tmp1,tmp5; \
|
100
|
+
movl 4+P(N),tmp2; \
|
101
|
+
xorl tmp5,R
|
102
|
+
|
103
|
+
#define BF_ENCRYPT_START \
|
104
|
+
BF_ROUND(L, R, 0); \
|
105
|
+
BF_ROUND(R, L, 1); \
|
106
|
+
BF_ROUND(L, R, 2); \
|
107
|
+
BF_ROUND(R, L, 3); \
|
108
|
+
BF_ROUND(L, R, 4); \
|
109
|
+
BF_ROUND(R, L, 5); \
|
110
|
+
BF_ROUND(L, R, 6); \
|
111
|
+
BF_ROUND(R, L, 7); \
|
112
|
+
BF_ROUND(L, R, 8); \
|
113
|
+
BF_ROUND(R, L, 9); \
|
114
|
+
BF_ROUND(L, R, 10); \
|
115
|
+
BF_ROUND(R, L, 11); \
|
116
|
+
BF_ROUND(L, R, 12); \
|
117
|
+
BF_ROUND(R, L, 13); \
|
118
|
+
BF_ROUND(L, R, 14); \
|
119
|
+
BF_ROUND(R, L, 15); \
|
120
|
+
movl BF_ptr,tmp5; \
|
121
|
+
xorl L,tmp2; \
|
122
|
+
movl P(17),L
|
123
|
+
|
124
|
+
#define BF_ENCRYPT_END \
|
125
|
+
xorl R,L; \
|
126
|
+
movl tmp2,R
|
127
|
+
|
128
|
+
DO_ALIGN(5)
|
129
|
+
.globl _BF_body_r
|
130
|
+
_BF_body_r:
|
131
|
+
movl 4(%esp),%eax
|
132
|
+
pushl %ebp
|
133
|
+
pushl %ebx
|
134
|
+
pushl %esi
|
135
|
+
pushl %edi
|
136
|
+
subl $BF_FRAME-8,%eax
|
137
|
+
xorl L,L
|
138
|
+
cmpl %esp,%eax
|
139
|
+
ja BF_die
|
140
|
+
xchgl %eax,%esp
|
141
|
+
xorl R,R
|
142
|
+
pushl %eax
|
143
|
+
leal 0x1000+BF_FRAME-4(ctx),%eax
|
144
|
+
movl 0x1000+BF_FRAME-4(ctx),tmp2
|
145
|
+
pushl %eax
|
146
|
+
xorl tmp3,tmp3
|
147
|
+
BF_loop_P:
|
148
|
+
BF_ENCRYPT_START
|
149
|
+
addl $8,tmp5
|
150
|
+
BF_ENCRYPT_END
|
151
|
+
leal 0x1000+18*4+BF_FRAME(ctx),tmp1
|
152
|
+
movl tmp5,BF_ptr
|
153
|
+
cmpl tmp5,tmp1
|
154
|
+
movl L,-8(tmp5)
|
155
|
+
movl R,-4(tmp5)
|
156
|
+
movl P(0),tmp2
|
157
|
+
ja BF_loop_P
|
158
|
+
leal BF_FRAME(ctx),tmp5
|
159
|
+
xorl tmp3,tmp3
|
160
|
+
movl tmp5,BF_ptr
|
161
|
+
BF_loop_S:
|
162
|
+
BF_ENCRYPT_START
|
163
|
+
BF_ENCRYPT_END
|
164
|
+
movl P(0),tmp2
|
165
|
+
movl L,(tmp5)
|
166
|
+
movl R,4(tmp5)
|
167
|
+
BF_ENCRYPT_START
|
168
|
+
BF_ENCRYPT_END
|
169
|
+
movl P(0),tmp2
|
170
|
+
movl L,8(tmp5)
|
171
|
+
movl R,12(tmp5)
|
172
|
+
BF_ENCRYPT_START
|
173
|
+
BF_ENCRYPT_END
|
174
|
+
movl P(0),tmp2
|
175
|
+
movl L,16(tmp5)
|
176
|
+
movl R,20(tmp5)
|
177
|
+
BF_ENCRYPT_START
|
178
|
+
addl $32,tmp5
|
179
|
+
BF_ENCRYPT_END
|
180
|
+
leal 0x1000+BF_FRAME(ctx),tmp1
|
181
|
+
movl tmp5,BF_ptr
|
182
|
+
cmpl tmp5,tmp1
|
183
|
+
movl P(0),tmp2
|
184
|
+
movl L,-8(tmp5)
|
185
|
+
movl R,-4(tmp5)
|
186
|
+
ja BF_loop_S
|
187
|
+
movl 4(%esp),%esp
|
188
|
+
popl %edi
|
189
|
+
popl %esi
|
190
|
+
popl %ebx
|
191
|
+
popl %ebp
|
192
|
+
ret
|
193
|
+
|
194
|
+
BF_die:
|
195
|
+
/* Oops, need to re-compile with a larger BF_FRAME. */
|
196
|
+
hlt
|
197
|
+
jmp BF_die
|
198
|
+
|
199
|
+
#endif
|
200
|
+
|
201
|
+
#if defined(__ELF__) && defined(__linux__)
|
202
|
+
.section .note.GNU-stack,"",%progbits
|
203
|
+
#endif
|
data/lib/bcrypt.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# A Ruby library implementing OpenBSD's bcrypt()/crypt_blowfish algorithm for
|
2
|
+
# hashing passwords.
|
3
|
+
module BCrypt
|
4
|
+
end
|
5
|
+
|
6
|
+
if RUBY_PLATFORM == "java"
|
7
|
+
require 'java'
|
8
|
+
else
|
9
|
+
require "openssl"
|
10
|
+
end
|
11
|
+
|
12
|
+
require "bcrypt_ext"
|
13
|
+
|
14
|
+
require 'bcrypt/error'
|
15
|
+
require 'bcrypt/engine'
|
16
|
+
require 'bcrypt/password'
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module BCrypt
|
2
|
+
# A Ruby wrapper for the bcrypt() C extension calls and the Java calls.
|
3
|
+
class Engine
|
4
|
+
# The default computational expense parameter.
|
5
|
+
DEFAULT_COST = 12
|
6
|
+
# The minimum cost supported by the algorithm.
|
7
|
+
MIN_COST = 4
|
8
|
+
# The maximum cost supported by the algorithm.
|
9
|
+
MAX_COST = 31
|
10
|
+
# Maximum possible size of bcrypt() salts.
|
11
|
+
MAX_SALT_LENGTH = 16
|
12
|
+
|
13
|
+
if RUBY_PLATFORM != "java"
|
14
|
+
# C-level routines which, if they don't get the right input, will crash the
|
15
|
+
# hell out of the Ruby process.
|
16
|
+
private_class_method :__bc_salt
|
17
|
+
private_class_method :__bc_crypt
|
18
|
+
end
|
19
|
+
|
20
|
+
@cost = nil
|
21
|
+
|
22
|
+
# Returns the cost factor that will be used if one is not specified when
|
23
|
+
# creating a password hash. Defaults to DEFAULT_COST if not set.
|
24
|
+
def self.cost
|
25
|
+
@cost || DEFAULT_COST
|
26
|
+
end
|
27
|
+
|
28
|
+
# Set a default cost factor that will be used if one is not specified when
|
29
|
+
# creating a password hash.
|
30
|
+
#
|
31
|
+
# Example:
|
32
|
+
#
|
33
|
+
# BCrypt::Engine::DEFAULT_COST #=> 12
|
34
|
+
# BCrypt::Password.create('secret').cost #=> 12
|
35
|
+
#
|
36
|
+
# BCrypt::Engine.cost = 8
|
37
|
+
# BCrypt::Password.create('secret').cost #=> 8
|
38
|
+
#
|
39
|
+
# # cost can still be overridden as needed
|
40
|
+
# BCrypt::Password.create('secret', :cost => 6).cost #=> 6
|
41
|
+
def self.cost=(cost)
|
42
|
+
@cost = cost
|
43
|
+
end
|
44
|
+
|
45
|
+
# Given a secret and a valid salt (see BCrypt::Engine.generate_salt) calculates
|
46
|
+
# a bcrypt() password hash.
|
47
|
+
def self.hash_secret(secret, salt, _ = nil)
|
48
|
+
if valid_secret?(secret)
|
49
|
+
if valid_salt?(salt)
|
50
|
+
if RUBY_PLATFORM == "java"
|
51
|
+
Java.bcrypt_jruby.BCrypt.hashpw(secret.to_s.to_java_bytes, salt.to_s)
|
52
|
+
else
|
53
|
+
__bc_crypt(secret.to_s, salt)
|
54
|
+
end
|
55
|
+
else
|
56
|
+
raise Errors::InvalidSalt.new("invalid salt")
|
57
|
+
end
|
58
|
+
else
|
59
|
+
raise Errors::InvalidSecret.new("invalid secret")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Generates a random salt with a given computational cost.
|
64
|
+
def self.generate_salt(cost = self.cost)
|
65
|
+
cost = cost.to_i
|
66
|
+
if cost > 0
|
67
|
+
if cost < MIN_COST
|
68
|
+
cost = MIN_COST
|
69
|
+
end
|
70
|
+
if RUBY_PLATFORM == "java"
|
71
|
+
Java.bcrypt_jruby.BCrypt.gensalt(cost)
|
72
|
+
else
|
73
|
+
prefix = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"
|
74
|
+
__bc_salt(prefix, cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH))
|
75
|
+
end
|
76
|
+
else
|
77
|
+
raise Errors::InvalidCost.new("cost must be numeric and > 0")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns true if +salt+ is a valid bcrypt() salt, false if not.
|
82
|
+
def self.valid_salt?(salt)
|
83
|
+
!!(salt =~ /^\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}$/)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns true if +secret+ is a valid bcrypt() secret, false if not.
|
87
|
+
def self.valid_secret?(secret)
|
88
|
+
secret.respond_to?(:to_s)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns the cost factor which will result in computation times less than +upper_time_limit_in_ms+.
|
92
|
+
#
|
93
|
+
# Example:
|
94
|
+
#
|
95
|
+
# BCrypt::Engine.calibrate(200) #=> 10
|
96
|
+
# BCrypt::Engine.calibrate(1000) #=> 12
|
97
|
+
#
|
98
|
+
# # should take less than 200ms
|
99
|
+
# BCrypt::Password.create("woo", :cost => 10)
|
100
|
+
#
|
101
|
+
# # should take less than 1000ms
|
102
|
+
# BCrypt::Password.create("woo", :cost => 12)
|
103
|
+
def self.calibrate(upper_time_limit_in_ms)
|
104
|
+
(BCrypt::Engine::MIN_COST..BCrypt::Engine::MAX_COST-1).each do |i|
|
105
|
+
start_time = Time.now
|
106
|
+
Password.create("testing testing", :cost => i+1)
|
107
|
+
end_time = Time.now - start_time
|
108
|
+
return i if end_time * 1_000 > upper_time_limit_in_ms
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Autodetects the cost from the salt string.
|
113
|
+
def self.autodetect_cost(salt)
|
114
|
+
salt[4..5].to_i
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
data/lib/bcrypt/error.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module BCrypt
|
2
|
+
|
3
|
+
class Error < StandardError # :nodoc:
|
4
|
+
end
|
5
|
+
|
6
|
+
module Errors # :nodoc:
|
7
|
+
|
8
|
+
# The salt parameter provided to bcrypt() is invalid.
|
9
|
+
class InvalidSalt < BCrypt::Error; end
|
10
|
+
|
11
|
+
# The hash parameter provided to bcrypt() is invalid.
|
12
|
+
class InvalidHash < BCrypt::Error; end
|
13
|
+
|
14
|
+
# The cost parameter provided to bcrypt() is invalid.
|
15
|
+
class InvalidCost < BCrypt::Error; end
|
16
|
+
|
17
|
+
# The secret parameter provided to bcrypt() is invalid.
|
18
|
+
class InvalidSecret < BCrypt::Error; end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module BCrypt
|
2
|
+
# A password management class which allows you to safely store users' passwords and compare them.
|
3
|
+
#
|
4
|
+
# Example usage:
|
5
|
+
#
|
6
|
+
# include BCrypt
|
7
|
+
#
|
8
|
+
# # hash a user's password
|
9
|
+
# @password = Password.create("my grand secret")
|
10
|
+
# @password #=> "$2a$12$C5.FIvVDS9W4AYZ/Ib37YuWd/7ozp1UaMhU28UKrfSxp2oDchbi3K"
|
11
|
+
#
|
12
|
+
# # store it safely
|
13
|
+
# @user.update_attribute(:password, @password)
|
14
|
+
#
|
15
|
+
# # read it back
|
16
|
+
# @user.reload!
|
17
|
+
# @db_password = Password.new(@user.password)
|
18
|
+
#
|
19
|
+
# # compare it after retrieval
|
20
|
+
# @db_password == "my grand secret" #=> true
|
21
|
+
# @db_password == "a paltry guess" #=> false
|
22
|
+
#
|
23
|
+
class Password < String
|
24
|
+
# The hash portion of the stored password hash.
|
25
|
+
attr_reader :checksum
|
26
|
+
# The salt of the store password hash (including version and cost).
|
27
|
+
attr_reader :salt
|
28
|
+
# The version of the bcrypt() algorithm used to create the hash.
|
29
|
+
attr_reader :version
|
30
|
+
# The cost factor used to create the hash.
|
31
|
+
attr_reader :cost
|
32
|
+
|
33
|
+
class << self
|
34
|
+
# Hashes a secret, returning a BCrypt::Password instance. Takes an optional <tt>:cost</tt> option, which is a
|
35
|
+
# logarithmic variable which determines how computational expensive the hash is to calculate (a <tt>:cost</tt> of
|
36
|
+
# 4 is twice as much work as a <tt>:cost</tt> of 3). The higher the <tt>:cost</tt> the harder it becomes for
|
37
|
+
# attackers to try to guess passwords (even if a copy of your database is stolen), but the slower it is to check
|
38
|
+
# users' passwords.
|
39
|
+
#
|
40
|
+
# Example:
|
41
|
+
#
|
42
|
+
# @password = BCrypt::Password.create("my secret", :cost => 13)
|
43
|
+
def create(secret, options = {})
|
44
|
+
cost = options[:cost] || BCrypt::Engine.cost
|
45
|
+
raise ArgumentError if cost > BCrypt::Engine::MAX_COST
|
46
|
+
Password.new(BCrypt::Engine.hash_secret(secret, BCrypt::Engine.generate_salt(cost)))
|
47
|
+
end
|
48
|
+
|
49
|
+
def valid_hash?(h)
|
50
|
+
h =~ /^\$[0-9a-z]{2}\$[0-9]{2}\$[A-Za-z0-9\.\/]{53}$/
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Initializes a BCrypt::Password instance with the data from a stored hash.
|
55
|
+
def initialize(raw_hash)
|
56
|
+
if valid_hash?(raw_hash)
|
57
|
+
self.replace(raw_hash)
|
58
|
+
@version, @cost, @salt, @checksum = split_hash(self)
|
59
|
+
else
|
60
|
+
raise Errors::InvalidHash.new("invalid hash")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise.
|
65
|
+
def ==(secret)
|
66
|
+
super(BCrypt::Engine.hash_secret(secret, @salt))
|
67
|
+
end
|
68
|
+
alias_method :is_password?, :==
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# Returns true if +h+ is a valid hash.
|
73
|
+
def valid_hash?(h)
|
74
|
+
self.class.valid_hash?(h)
|
75
|
+
end
|
76
|
+
|
77
|
+
# call-seq:
|
78
|
+
# split_hash(raw_hash) -> version, cost, salt, hash
|
79
|
+
#
|
80
|
+
# Splits +h+ into version, cost, salt, and hash and returns them in that order.
|
81
|
+
def split_hash(h)
|
82
|
+
_, v, c, mash = h.split('$')
|
83
|
+
return v.to_str, c.to_i, h[0, 29].to_str, mash[-31, 31].to_str
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|