bcrypt 3.1.12 → 3.1.18

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/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/engine.rb CHANGED
@@ -2,9 +2,19 @@ module BCrypt
2
2
  # A Ruby wrapper for the bcrypt() C extension calls and the Java calls.
3
3
  class Engine
4
4
  # The default computational expense parameter.
5
- DEFAULT_COST = 10
5
+ DEFAULT_COST = 12
6
6
  # The minimum cost supported by the algorithm.
7
7
  MIN_COST = 4
8
+ # The maximum cost supported by the algorithm.
9
+ MAX_COST = 31
10
+ # Maximum possible size of bcrypt() secrets.
11
+ # Older versions of the bcrypt library would truncate passwords longer
12
+ # than 72 bytes, but newer ones do not. We truncate like the old library for
13
+ # forward compatibility. This way users upgrading from Ubuntu 18.04 to 20.04
14
+ # will not have their user passwords invalidated, for example.
15
+ # A max secret length greater than 255 leads to bcrypt returning nil.
16
+ # https://github.com/bcrypt-ruby/bcrypt-ruby/issues/225#issuecomment-875908425
17
+ MAX_SECRET_BYTESIZE = 72
8
18
  # Maximum possible size of bcrypt() salts.
9
19
  MAX_SALT_LENGTH = 16
10
20
 
@@ -28,8 +38,8 @@ module BCrypt
28
38
  #
29
39
  # Example:
30
40
  #
31
- # BCrypt::Engine::DEFAULT_COST #=> 10
32
- # BCrypt::Password.create('secret').cost #=> 10
41
+ # BCrypt::Engine::DEFAULT_COST #=> 12
42
+ # BCrypt::Password.create('secret').cost #=> 12
33
43
  #
34
44
  # BCrypt::Engine.cost = 8
35
45
  # BCrypt::Password.create('secret').cost #=> 8
@@ -41,14 +51,16 @@ module BCrypt
41
51
  end
42
52
 
43
53
  # Given a secret and a valid salt (see BCrypt::Engine.generate_salt) calculates
44
- # a bcrypt() password hash.
54
+ # a bcrypt() password hash. Secrets longer than 72 bytes are truncated.
45
55
  def self.hash_secret(secret, salt, _ = nil)
46
56
  if valid_secret?(secret)
47
57
  if valid_salt?(salt)
48
58
  if RUBY_PLATFORM == "java"
49
- Java.bcrypt_jruby.BCrypt.hashpw(secret.to_s, salt.to_s)
59
+ Java.bcrypt_jruby.BCrypt.hashpw(secret.to_s.to_java_bytes, salt.to_s)
50
60
  else
51
- __bc_crypt(secret.to_s, salt)
61
+ secret = secret.to_s
62
+ secret = secret.byteslice(0, MAX_SECRET_BYTESIZE) if secret && secret.bytesize > MAX_SECRET_BYTESIZE
63
+ __bc_crypt(secret, salt)
52
64
  end
53
65
  else
54
66
  raise Errors::InvalidSalt.new("invalid salt")
@@ -68,8 +80,7 @@ module BCrypt
68
80
  if RUBY_PLATFORM == "java"
69
81
  Java.bcrypt_jruby.BCrypt.gensalt(cost)
70
82
  else
71
- prefix = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"
72
- __bc_salt(prefix, cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH))
83
+ __bc_salt("$2a$", cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH))
73
84
  end
74
85
  else
75
86
  raise Errors::InvalidCost.new("cost must be numeric and > 0")
@@ -78,7 +89,7 @@ module BCrypt
78
89
 
79
90
  # Returns true if +salt+ is a valid bcrypt() salt, false if not.
80
91
  def self.valid_salt?(salt)
81
- !!(salt =~ /^\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}$/)
92
+ !!(salt =~ /\A\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}\z/)
82
93
  end
83
94
 
84
95
  # Returns true if +secret+ is a valid bcrypt() secret, false if not.
@@ -99,7 +110,7 @@ module BCrypt
99
110
  # # should take less than 1000ms
100
111
  # BCrypt::Password.create("woo", :cost => 12)
101
112
  def self.calibrate(upper_time_limit_in_ms)
102
- 40.times do |i|
113
+ (BCrypt::Engine::MIN_COST..BCrypt::Engine::MAX_COST-1).each do |i|
103
114
  start_time = Time.now
104
115
  Password.create("testing testing", :cost => i+1)
105
116
  end_time = Time.now - start_time
@@ -7,7 +7,7 @@ module BCrypt
7
7
  #
8
8
  # # hash a user's password
9
9
  # @password = Password.create("my grand secret")
10
- # @password #=> "$2a$10$GtKs1Kbsig8ULHZzO1h2TetZfhO4Fmlxphp8bVKnUlZCBYYClPohG"
10
+ # @password #=> "$2a$12$C5.FIvVDS9W4AYZ/Ib37YuWd/7ozp1UaMhU28UKrfSxp2oDchbi3K"
11
11
  #
12
12
  # # store it safely
13
13
  # @user.update_attribute(:password, @password)
@@ -42,12 +42,12 @@ module BCrypt
42
42
  # @password = BCrypt::Password.create("my secret", :cost => 13)
43
43
  def create(secret, options = {})
44
44
  cost = options[:cost] || BCrypt::Engine.cost
45
- raise ArgumentError if cost > 31
45
+ raise ArgumentError if cost > BCrypt::Engine::MAX_COST
46
46
  Password.new(BCrypt::Engine.hash_secret(secret, BCrypt::Engine.generate_salt(cost)))
47
47
  end
48
48
 
49
49
  def valid_hash?(h)
50
- h =~ /^\$[0-9a-z]{2}\$[0-9]{2}\$[A-Za-z0-9\.\/]{53}$/
50
+ /\A\$[0-9a-z]{2}\$[0-9]{2}\$[A-Za-z0-9\.\/]{53}\z/ === h
51
51
  end
52
52
  end
53
53
 
@@ -62,6 +62,17 @@ module BCrypt
62
62
  end
63
63
 
64
64
  # Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise.
65
+ #
66
+ # Comparison edge case/gotcha:
67
+ #
68
+ # secret = "my secret"
69
+ # @password = BCrypt::Password.create(secret)
70
+ #
71
+ # @password == secret # => True
72
+ # @password == @password # => False
73
+ # @password == @password.to_s # => False
74
+ # @password.to_s == @password # => True
75
+ # @password.to_s == @password.to_s # => True
65
76
  def ==(secret)
66
77
  super(BCrypt::Engine.hash_secret(secret, @salt))
67
78
  end
@@ -83,5 +94,4 @@ module BCrypt
83
94
  return v.to_str, c.to_i, h[0, 29].to_str, mash[-31, 31].to_str
84
95
  end
85
96
  end
86
-
87
97
  end
data/lib/bcrypt.rb CHANGED
@@ -9,12 +9,7 @@ else
9
9
  require "openssl"
10
10
  end
11
11
 
12
- begin
13
- RUBY_VERSION =~ /(\d+.\d+)/
14
- require "#{$1}/bcrypt_ext"
15
- rescue LoadError
16
- require "bcrypt_ext"
17
- end
12
+ require "bcrypt_ext"
18
13
 
19
14
  require 'bcrypt/error'
20
15
  require 'bcrypt/engine'
@@ -1,9 +1,23 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2
+ require 'securerandom'
3
+
4
+ describe 'BCrypt::Engine' do
5
+ describe '.calibrate(upper_time_limit_in_ms)' do
6
+ context 'a tiny upper time limit provided' do
7
+ it 'returns a minimum cost supported by the algorithm' do
8
+ expect(BCrypt::Engine.calibrate(0.001)).to eq(4)
9
+ end
10
+ end
11
+ end
12
+ end
2
13
 
3
14
  describe "The BCrypt engine" do
4
15
  specify "should calculate the optimal cost factor to fit in a specific time" do
5
- first = BCrypt::Engine.calibrate(100)
6
- second = BCrypt::Engine.calibrate(400)
16
+ start_time = Time.now
17
+ BCrypt::Password.create("testing testing", :cost => BCrypt::Engine::MIN_COST + 1)
18
+ min_time_ms = (Time.now - start_time) * 1000
19
+ first = BCrypt::Engine.calibrate(min_time_ms)
20
+ second = BCrypt::Engine.calibrate(min_time_ms * 4)
7
21
  expect(second).to be > first
8
22
  end
9
23
  end
@@ -67,16 +81,96 @@ describe "Generating BCrypt hashes" do
67
81
  end
68
82
 
69
83
  specify "should be interoperable with other implementations" do
70
- # test vectors from the OpenWall implementation <http://www.openwall.com/crypt/>
71
84
  test_vectors = [
85
+ # test vectors from the OpenWall implementation <https://www.openwall.com/crypt/>, found in wrapper.c
72
86
  ["U*U", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"],
73
87
  ["U*U*", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK"],
74
88
  ["U*U*U", "$2a$05$XXXXXXXXXXXXXXXXXXXXXO", "$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a"],
89
+ ["0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789chars after 72 are ignored", "$2a$05$abcdefghijklmnopqrstuu", "$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui"],
90
+ ["\xa3", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e"],
91
+ ["\xff\xff\xa3", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e"],
92
+ ["\xff\xff\xa3", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e"],
93
+ ["\xff\xff\xa3", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.nqd1wy.pTMdcvrRWxyiGL2eMz.2a85."],
94
+ ["\xff\xff\xa3", "$2b$05$/OK.fbVrR/bpIqNJ5ianF.", "$2b$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e"],
95
+ ["\xa3", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq"],
96
+ ["\xa3", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq"],
97
+ ["\xa3", "$2b$05$/OK.fbVrR/bpIqNJ5ianF.", "$2b$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq"],
98
+ ["1\xa3" "345", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi"],
99
+ ["\xff\xa3" "345", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi"],
100
+ ["\xff\xa3" "34" "\xff\xff\xff\xa3" "345", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi"],
101
+ ["\xff\xa3" "34" "\xff\xff\xff\xa3" "345", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi"],
102
+ ["\xff\xa3" "34" "\xff\xff\xff\xa3" "345", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.ZC1JEJ8Z4gPfpe1JOr/oyPXTWl9EFd."],
103
+ ["\xff\xa3" "345", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e"],
104
+ ["\xff\xa3" "345", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e"],
105
+ ["\xa3" "ab", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS"],
106
+ ["\xa3" "ab", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.", "$2x$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS"],
107
+ ["\xa3" "ab", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.", "$2y$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS"],
108
+ ["\xd1\x91", "$2x$05$6bNw2HLQYeqHYyBfLMsv/O", "$2x$05$6bNw2HLQYeqHYyBfLMsv/OiwqTymGIGzFsA4hOTWebfehXHNprcAS"],
109
+ ["\xd0\xc1\xd2\xcf\xcc\xd8", "$2x$05$6bNw2HLQYeqHYyBfLMsv/O", "$2x$05$6bNw2HLQYeqHYyBfLMsv/O9LIGgn8OMzuDoHfof8AQimSGfcSWxnS"],
110
+ ["\xaa"*72+"chars after 72 are ignored as usual", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6"],
111
+ ["\xaa\x55"*36, "$2a$05$/OK.fbVrR/bpIqNJ5ianF.", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy"],
112
+ ["\x55\xaa\xff"*24, "$2a$05$/OK.fbVrR/bpIqNJ5ianF.", "$2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe"],
75
113
  ["", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.", "$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy"],
76
- ["0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", "$2a$05$abcdefghijklmnopqrstuu", "$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui"]
114
+
115
+ # test vectors from the Java implementation, found in https://github.com/spring-projects/spring-security/blob/master/crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptTests.java
116
+ ["", "$2a$06$DCq7YPn5Rq63x1Lad4cll.", "$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s."],
117
+ ["", "$2a$08$HqWuK6/Ng6sg9gQzbLrgb.", "$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye"],
118
+ ["", "$2a$10$k1wbIrmNyFAPwPVPSVa/ze", "$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW"],
119
+ ["", "$2a$12$k42ZFHFWqBp3vWli.nIn8u", "$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO"],
120
+ ["", "$2b$06$8eVN9RiU8Yki430X.wBvN.", "$2b$06$8eVN9RiU8Yki430X.wBvN.LWaqh2962emLVSVXVZIXJvDYLsV0oFu"],
121
+ ["", "$2b$06$NlgfNgpIc6GlHciCkMEW8u", "$2b$06$NlgfNgpIc6GlHciCkMEW8uKOBsyvAp7QwlHpysOlKdtyEw50WQua2"],
122
+ ["", "$2y$06$mFDtkz6UN7B3GZ2qi2hhaO", "$2y$06$mFDtkz6UN7B3GZ2qi2hhaO3OFWzNEdcY84ELw6iHCPruuQfSAXBLK"],
123
+ ["", "$2y$06$88kSqVttBx.e9iXTPCLa5u", "$2y$06$88kSqVttBx.e9iXTPCLa5uFPrVFjfLH4D.KcO6pBiAmvUkvdg0EYy"],
124
+ ["a", "$2a$06$m0CrhHm10qJ3lXRY.5zDGO", "$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe"],
125
+ ["a", "$2a$08$cfcvVd2aQ8CMvoMpP2EBfe", "$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V."],
126
+ ["a", "$2a$10$k87L/MF28Q673VKh8/cPi.", "$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u"],
127
+ ["a", "$2a$12$8NJH3LsPrANStV6XtBakCe", "$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS"],
128
+ ["a", "$2b$06$ehKGYiS4wt2HAr7KQXS5z.", "$2b$06$ehKGYiS4wt2HAr7KQXS5z.OaRjB4jHO7rBHJKlGXbqEH3QVJfO7iO"],
129
+ ["a", "$2b$06$PWxFFHA3HiCD46TNOZh30e", "$2b$06$PWxFFHA3HiCD46TNOZh30eNto1hg5uM9tHBlI4q/b03SW/gGKUYk6"],
130
+ ["a", "$2y$06$LUdD6/aD0e/UbnxVAVbvGu", "$2y$06$LUdD6/aD0e/UbnxVAVbvGuUmIoJ3l/OK94ThhadpMWwKC34LrGEey"],
131
+ ["a", "$2y$06$eqgY.T2yloESMZxgp76deO", "$2y$06$eqgY.T2yloESMZxgp76deOROa7nzXDxbO0k.PJvuClTa.Vu1AuemG"],
132
+ ["abc", "$2a$06$If6bvum7DFjUnE9p2uDeDu", "$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i"],
133
+ ["abc", "$2a$08$Ro0CUfOqk6cXEKf3dyaM7O", "$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm"],
134
+ ["abc", "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.", "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi"],
135
+ ["abc", "$2a$12$EXRkfkdmXn2gzds2SSitu.", "$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q"],
136
+ ["abc", "$2b$06$5FyQoicpbox1xSHFfhhdXu", "$2b$06$5FyQoicpbox1xSHFfhhdXuR2oxLpO1rYsQh5RTkI/9.RIjtoF0/ta"],
137
+ ["abc", "$2b$06$1kJyuho8MCVP3HHsjnRMkO", "$2b$06$1kJyuho8MCVP3HHsjnRMkO1nvCOaKTqLnjG2TX1lyMFbXH/aOkgc."],
138
+ ["abc", "$2y$06$ACfku9dT6.H8VjdKb8nhlu", "$2y$06$ACfku9dT6.H8VjdKb8nhluaoBmhJyK7GfoNScEfOfrJffUxoUeCjK"],
139
+ ["abc", "$2y$06$9JujYcoWPmifvFA3RUP90e", "$2y$06$9JujYcoWPmifvFA3RUP90e5rSEHAb5Ye6iv3.G9ikiHNv5cxjNEse"],
140
+ ["abcdefghijklmnopqrstuvwxyz", "$2a$06$.rCVZVOThsIa97pEDOxvGu", "$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC"],
141
+ ["abcdefghijklmnopqrstuvwxyz", "$2a$08$aTsUwsyowQuzRrDqFflhge", "$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz."],
142
+ ["abcdefghijklmnopqrstuvwxyz", "$2a$10$fVH8e28OQRj9tqiDXs1e1u", "$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq"],
143
+ ["abcdefghijklmnopqrstuvwxyz", "$2a$12$D4G5f18o7aMMfwasBL7Gpu", "$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG"],
144
+ ["abcdefghijklmnopqrstuvwxyz", "$2b$06$O8E89AQPj1zJQA05YvIAU.", "$2b$06$O8E89AQPj1zJQA05YvIAU.hMpj25BXri1bupl/Q7CJMlpLwZDNBoO"],
145
+ ["abcdefghijklmnopqrstuvwxyz", "$2b$06$PDqIWr./o/P3EE/P.Q0A/u", "$2b$06$PDqIWr./o/P3EE/P.Q0A/uFg86WL/PXTbaW267TDALEwDylqk00Z."],
146
+ ["abcdefghijklmnopqrstuvwxyz", "$2y$06$34MG90ZLah8/ZNr3ltlHCu", "$2y$06$34MG90ZLah8/ZNr3ltlHCuz6bachF8/3S5jTuzF1h2qg2cUk11sFW"],
147
+ ["abcdefghijklmnopqrstuvwxyz", "$2y$06$AK.hSLfMyw706iEW24i68u", "$2y$06$AK.hSLfMyw706iEW24i68uKAc2yorPTrB0cimvjJHEBUrPkOq7VvG"],
148
+ ["~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$06$fPIsBO8qRqkjj273rfaOI.", "$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO"],
149
+ ["~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$08$Eq2r4G/76Wv39MzSX262hu", "$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW"],
150
+ ["~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$10$LgfYWkbzEvQ4JakH7rOvHe", "$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS"],
151
+ ["~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$12$WApznUOJfkEGSmYRfnkrPO", "$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC"],
152
+ ["~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2b$06$FGWA8OlY6RtQhXBXuCJ8Wu", "$2b$06$FGWA8OlY6RtQhXBXuCJ8WusVipRI15cWOgJK8MYpBHEkktMfbHRIG"],
153
+ ["~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2b$06$G6aYU7UhUEUDJBdTgq3CRe", "$2b$06$G6aYU7UhUEUDJBdTgq3CRekiopCN4O4sNitFXrf5NUscsVZj3a2r6"],
154
+ ["~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2y$06$sYDFHqOcXTjBgOsqC0WCKe", "$2y$06$sYDFHqOcXTjBgOsqC0WCKeMd3T1UhHuWQSxncLGtXDLMrcE6vFDti"],
155
+ ["~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2y$06$6Xm0gCw4g7ZNDCEp4yTise", "$2y$06$6Xm0gCw4g7ZNDCEp4yTisez0kSdpXEl66MvdxGidnmChIe8dFmMnq"]
77
156
  ]
78
157
  for secret, salt, test_vector in test_vectors
79
158
  expect(BCrypt::Engine.hash_secret(secret, salt)).to eql(test_vector)
80
159
  end
81
160
  end
161
+
162
+ specify "should truncate long 1-byte character secrets to 72 bytes" do
163
+ # 'b' as a base triggers the failure at 256 characters, but 'a' does not.
164
+ too_long_secret = 'b'*(BCrypt::Engine::MAX_SECRET_BYTESIZE + 1)
165
+ just_right_secret = 'b'*BCrypt::Engine::MAX_SECRET_BYTESIZE
166
+ expect(BCrypt::Engine.hash_secret(too_long_secret, @salt)).to eq(BCrypt::Engine.hash_secret(just_right_secret, @salt))
167
+ end
168
+
169
+ specify "should truncate long multi-byte character secrets to 72 bytes" do
170
+ # 256 times causes bcrypt to return nil for libxcrypt > 4.4.18-4.
171
+ too_long_secret = '𐐷'*256
172
+ # 𐐷 takes 4 bytes in UTF-8. 18 times is 72 bytes
173
+ just_right_secret = '𐐷'*18
174
+ expect(BCrypt::Engine.hash_secret(too_long_secret, @salt)).to eq(BCrypt::Engine.hash_secret(just_right_secret, @salt))
175
+ end
82
176
  end
@@ -1,4 +1,5 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2
+ require 'securerandom'
2
3
 
3
4
  describe "Creating a hashed password" do
4
5
 
@@ -26,6 +27,10 @@ describe "Creating a hashed password" do
26
27
  expect { BCrypt::Password.create( "" ) }.not_to raise_error
27
28
  expect { BCrypt::Password.create( String.new ) }.not_to raise_error
28
29
  end
30
+
31
+ specify "should tolerate very long string secrets" do
32
+ expect { BCrypt::Password.create("abcd"*1024) }.not_to raise_error
33
+ end
29
34
  end
30
35
 
31
36
  describe "Reading a hashed password" do
@@ -108,6 +113,7 @@ end
108
113
  describe "Validating a generated salt" do
109
114
  specify "should not accept an invalid salt" do
110
115
  expect(BCrypt::Engine.valid_salt?("invalid")).to eq(false)
116
+ expect(BCrypt::Engine.valid_salt?("invalid\n#{BCrypt::Engine.generate_salt}\ninvalid")).to eq(false)
111
117
  end
112
118
  specify "should accept a valid salt" do
113
119
  expect(BCrypt::Engine.valid_salt?(BCrypt::Engine.generate_salt)).to eq(true)
@@ -116,9 +122,10 @@ end
116
122
 
117
123
  describe "Validating a password hash" do
118
124
  specify "should not accept an invalid password" do
119
- expect(BCrypt::Password.valid_hash?("i_am_so_not_valid")).to be_falsey
125
+ expect(BCrypt::Password.valid_hash?("i_am_so_not_valid")).to be(false)
126
+ expect(BCrypt::Password.valid_hash?("invalid\n#{BCrypt::Password.create "i_am_so_valid"}\ninvalid")).to be(false)
120
127
  end
121
128
  specify "should accept a valid password" do
122
- expect(BCrypt::Password.valid_hash?(BCrypt::Password.create "i_am_so_valid")).to be_truthy
129
+ expect(BCrypt::Password.valid_hash?(BCrypt::Password.create "i_am_so_valid")).to be(true)
123
130
  end
124
131
  end
metadata CHANGED
@@ -1,60 +1,47 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bcrypt
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.12
4
+ version: 3.1.18
5
5
  platform: ruby
6
6
  authors:
7
7
  - Coda Hale
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-16 00:00:00.000000000 Z
11
+ date: 2022-05-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.9.2
19
+ version: 1.2.0
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.9.2
26
+ version: 1.2.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ! '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '3'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ! '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3'
41
- - !ruby/object:Gem::Dependency
42
- name: rdoc
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ~>
46
- - !ruby/object:Gem::Version
47
- version: '3.12'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ~>
53
- - !ruby/object:Gem::Version
54
- version: '3.12'
55
- description: ! " bcrypt() is a sophisticated and secure hash algorithm designed
56
- by The OpenBSD project\n for hashing passwords. The bcrypt Ruby gem provides
57
- a simple wrapper for safely handling\n passwords.\n"
41
+ description: |2
42
+ bcrypt() is a sophisticated and secure hash algorithm designed by The OpenBSD project
43
+ for hashing passwords. The bcrypt Ruby gem provides a simple wrapper for safely handling
44
+ passwords.
58
45
  email: coda.hale@gmail.com
59
46
  executables: []
60
47
  extensions:
@@ -68,26 +55,27 @@ extra_rdoc_files:
68
55
  - lib/bcrypt/error.rb
69
56
  - lib/bcrypt.rb
70
57
  files:
71
- - .gitignore
72
- - .rspec
73
- - .travis.yml
58
+ - ".github/workflows/ruby.yml"
59
+ - ".gitignore"
60
+ - ".rspec"
74
61
  - CHANGELOG
75
62
  - COPYING
76
63
  - Gemfile
77
- - Gemfile.lock
78
64
  - README.md
79
65
  - Rakefile
80
- - appveyor.yml
81
66
  - bcrypt.gemspec
82
67
  - ext/jruby/bcrypt_jruby/BCrypt.java
83
68
  - ext/mri/bcrypt_ext.c
84
69
  - ext/mri/crypt.c
85
70
  - ext/mri/crypt.h
86
71
  - ext/mri/crypt_blowfish.c
72
+ - ext/mri/crypt_blowfish.h
87
73
  - ext/mri/crypt_gensalt.c
74
+ - ext/mri/crypt_gensalt.h
88
75
  - ext/mri/extconf.rb
89
76
  - ext/mri/ow-crypt.h
90
77
  - ext/mri/wrapper.c
78
+ - ext/mri/x86.S
91
79
  - lib/bcrypt.rb
92
80
  - lib/bcrypt/engine.rb
93
81
  - lib/bcrypt/error.rb
@@ -97,33 +85,32 @@ files:
97
85
  - spec/bcrypt/error_spec.rb
98
86
  - spec/bcrypt/password_spec.rb
99
87
  - spec/spec_helper.rb
100
- homepage: https://github.com/codahale/bcrypt-ruby
88
+ homepage: https://github.com/bcrypt-ruby/bcrypt-ruby
101
89
  licenses:
102
90
  - MIT
103
91
  metadata: {}
104
92
  post_install_message:
105
93
  rdoc_options:
106
- - --title
94
+ - "--title"
107
95
  - bcrypt-ruby
108
- - --line-numbers
109
- - --inline-source
110
- - --main
96
+ - "--line-numbers"
97
+ - "--inline-source"
98
+ - "--main"
111
99
  - README.md
112
100
  require_paths:
113
101
  - lib
114
102
  required_ruby_version: !ruby/object:Gem::Requirement
115
103
  requirements:
116
- - - ! '>='
104
+ - - ">="
117
105
  - !ruby/object:Gem::Version
118
106
  version: '0'
119
107
  required_rubygems_version: !ruby/object:Gem::Requirement
120
108
  requirements:
121
- - - ! '>='
109
+ - - ">="
122
110
  - !ruby/object:Gem::Version
123
111
  version: '0'
124
112
  requirements: []
125
- rubyforge_project:
126
- rubygems_version: 2.7.6
113
+ rubygems_version: 3.1.4
127
114
  signing_key:
128
115
  specification_version: 4
129
116
  summary: OpenBSD's bcrypt() password hashing algorithm.
data/.travis.yml DELETED
@@ -1,21 +0,0 @@
1
- language: ruby
2
- before_install:
3
- - gem update --system
4
- - gem install bundler
5
- rvm:
6
- - 1.8
7
- - 1.9
8
- - 2.0
9
- - 2.1
10
- - 2.2
11
- - 2.3
12
- - 2.4
13
- - 2.5
14
- - 2.6
15
- - ruby-head
16
- - jruby-18mode
17
- - jruby-19mode
18
- - jruby-head
19
- - rbx-3
20
- - ree
21
- script: bundle exec rake