bcrypt 3.1.13 → 3.1.22

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8d5e2ce3f7df5807725e03678a1d8d60d24d710aefa7a3c2636c4c71d7602911
4
- data.tar.gz: 98bcb12a01f59e508d9d523dd031bab988d453abaddde47847c34e7919b67c23
3
+ metadata.gz: 521c5039d4a683bdf17faa98c3fddc47318f415597bd7575615c1f309ba65a4d
4
+ data.tar.gz: 9abdb8766bcfdc8cfeacbe41eb66fd0a8436ad5b1e9ff67b18239019387be3a1
5
5
  SHA512:
6
- metadata.gz: 2f3bc43174cdb2f8298c81c7853c973e33f745aa1ea5ddb69121ebd953b086494005dce8a9c1861e818f38a1084c301df302ee8821ae975be2186ddbc7b07c80
7
- data.tar.gz: 9380402fbc0260e1b0590e846795324144c6d8b70c16c8a8ff3edbb93297387ee452bd1cbcd0b66126f48e99d11433ad958c1ddb4b801e586752684ad52d523f
6
+ metadata.gz: 6ce98e4f36915b8fb3dc2cc5a0dadb7624914bbfabd8d5e804ac1c945c8fe23794c1a3652841634766c5fe8876cd06ccb04b737c84f54e6b4bca82701d529c07
7
+ data.tar.gz: 9a2eddcb94fa016dfae3e46901df0cd5f9afd30c63789eb4682cf9fc7b55cc3348e1822663a4c4cff9f277d26a6f907ba139d2578fb1510b58559ee76d58d2ce
data/CHANGELOG CHANGED
@@ -1,94 +1,127 @@
1
- 1.0.0 Feb 27 2007
2
- - Initial release.
1
+ 3.1.22 Mar 18 2026
2
+ - [CVE-2026-33306] Fix integer overflow in Java extension
3
3
 
4
- 2.0.0 Mar 07 2007
5
- - Removed BCrypt::Password#exactly_equals -- use BCrypt::Password#eql? instead.
6
- - Added BCrypt::Password#is_password?.
7
- - Refactored out BCrypt::Internals into more useful BCrypt::Engine.
8
- - Added validation of secrets -- nil is not healthy.
4
+ 3.1.21 Dec 31 2025
5
+ - Use constant time comparisons
6
+ - Mark as Ractor safe
9
7
 
10
- 2.0.1 Mar 09 2007
11
- - Fixed load path issues
12
- - Fixed crashes when hashing weird values (e.g., false, etc.)
8
+ 3.1.20 Nov 17 2023
9
+ - Limit packaged files -- decrease gem filesize by ~28% [GH #272 by @pusewicz]
13
10
 
14
- 2.0.2 Jun 06 2007
15
- - Fixed example code in the README [Winson]
16
- - Fixed Solaris compatibility [Jeremy LaTrasse, Twitter crew]
11
+ 3.1.19 June 22 2023
12
+ - Deprecate passing the third argument to `BCrypt::Engine.hash_secret` [GH #207 by @sergey-alekseev]
13
+ - Add GC guards so the C compiler won't optimize out references [GH #270]
17
14
 
18
- 2.0.3 May 07 2008
19
- - Made exception classes descend from StandardError, not Exception [Dan42]
20
- - Changed BCrypt::Engine.hash to BCrypt::Engine.hash_secret to avoid Merb
21
- sorting issues. [Lee Pope]
15
+ 3.1.18 May 16 2022
16
+ - Unlock GVL when calculating hashes and salts [GH #260]
17
+ - Fix compilation warnings in `ext/mri/bcrypt_ext.c` [GH #261]
22
18
 
23
- 2.0.4 Mar 09 2009
24
- - Added Ruby 1.9 compatibility. [Genki Takiuchi]
25
- - Fixed segfaults on some different types of empty strings. [Mike Pomraning]
19
+ 3.1.17 Mar 14 2022
20
+ - Fix regex in validators to use \A and \z instead of ^ and $ [GH #121]
21
+ - Truncate secrets greater than 72 bytes in hash_secret [GH #255]
22
+ - Assorted test and doc improvements
26
23
 
27
- 2.0.5 Mar 11 2009
28
- - Fixed Ruby 1.8.5 compatibility. [Mike Pomraning]
24
+ 3.1.16 Sep 3 2020
25
+ - Fix compilation on FreeBSD. [GH #234]
29
26
 
30
- 2.1.0 Aug 12 2009
31
- - Improved code coverage, unit tests, and build chain. [Hongli Lai]
32
- - Ruby 1.9 compatibility fixes. [Hongli Lai]
33
- - JRuby support, using Damien Miller's jBCrypt. [Hongli Lai]
34
- - Ruby 1.9 GIL releasing for high-cost hashes. [Hongli Lai]
27
+ 3.1.15 July 21 2020
28
+ - Remove GVL optimization. Apparently it breaks things [GH #230]
35
29
 
36
- 2.1.1 Aug 14 2009
37
- - JVM 1.4/1.5 compatibility [Hongli Lai]
30
+ 3.1.14 July 21 2020
31
+ - Start calibration from the minimum cost supported by the algorithm [GH #206 by @sergey-alekseev]
38
32
 
39
- 2.1.2 Sep 16 2009
40
- - Fixed support for Solaris, OpenSolaris.
33
+ 3.1.13 May 31 2019
34
+ - No longer include compiled binaries for Windows. See GH #173.
35
+ - Update C and Java implementations to latest versions [GH #182 by @fonica]
36
+ - Bump default cost to 12 [GH #181 by @bdewater]
37
+ - Remove explicit support for Rubies 1.8 and 1.9
38
+ - Define SKIP_GNU token when building extension (Fixes FreeBSD >= 12) [GH #189 by @adam12]
41
39
 
42
- 3.0.0 Aug 24 2011
43
- - Bcrypt C implementation replaced with a public domain implementation.
44
- - License changed to MIT
40
+ 3.1.12 May 16 2018
41
+ - Add support for Ruby 2.3, 2.4, and 2.5 in compiled Windows binaries
42
+ - Fix compatibility with libxcrypt - Fixes hash errors in Fedora 28 and Ubuntu 20 [GH #164 by @besser82]
45
43
 
46
- 3.0.1 Sep 12 2011
47
- - create raises an exception if the cost is higher than 31. GH #27
44
+ 3.1.11 Mar 06 2016
45
+ - Add support for Ruby 2.2 in compiled Windows binaries
48
46
 
49
- 3.1.0 May 07 2013
50
- - Add BCrypt::Password.valid_hash?(str) to check if a string is a valid bcrypt password hash
51
- - BCrypt::Password cost should be set to DEFAULT_COST if nil
52
- - Add BCrypt::Engine.cost attribute for getting/setting a default cost externally
47
+ 3.1.10 Jan 28 2015
48
+ - Fix issue with dumping a BCrypt::Password instance to YAML in Ruby 2.2 [GH #107 by @mattwildig]
53
49
 
54
- 3.1.1 Jul 10 2013
55
- - Remove support for Ruby 1.8 in compiled win32 binaries
50
+ 3.1.9 Oct 23 2014
51
+ - Rebuild corrupt binaries
56
52
 
57
- 3.1.2 Aug 26 2013
58
- - Add support for Ruby 1.8 and 2.0 (in addition to 1.9) in compiled Windows binaries
59
- - Add support for 64-bit Windows
53
+ 3.1.8 Oct 23 2014
54
+ - Add support for Ruby 2.1 in compiled Windows binaries [GH #102]
60
55
 
61
- 3.1.3 Feb 21 2014
62
- - Add support for Ruby 2.1 in compiled Windows binaries
63
- - Rename gem from "bcrypt-ruby" to just "bcrypt". [GH #86 by @sferik]
56
+ 3.1.7 Feb 24 2014
57
+ - Rebuild corrupt Java binary version of gem [GH #90]
58
+ - The 2.1 support for Windows binaries alleged in 3.1.3 was a lie -- documentation removed
64
59
 
65
60
  3.1.6 Feb 21 2014
66
61
  - Dummy version of "bcrypt-ruby" needed a couple version bumps to fix some
67
62
  bugs. It felt wrong to have that at a higher version than the real gem, so
68
63
  the real gem is getting bumped to 3.1.6.
69
64
 
70
- 3.1.7 Feb 24 2014
71
- - Rebuild corrupt Java binary version of gem [GH #90]
72
- - The 2.1 support for Windows binaries alleged in 3.1.3 was a lie -- documentation removed
65
+ 3.1.3 Feb 21 2014
66
+ - Add support for Ruby 2.1 in compiled Windows binaries
67
+ - Rename gem from "bcrypt-ruby" to just "bcrypt". [GH #86 by @sferik]
73
68
 
74
- 3.1.8 Oct 23 2014
75
- - Add support for Ruby 2.1 in compiled Windows binaries [GH #102]
69
+ 3.1.2 Aug 26 2013
70
+ - Add support for Ruby 1.8 and 2.0 (in addition to 1.9) in compiled Windows binaries
71
+ - Add support for 64-bit Windows
76
72
 
77
- 3.1.9 Oct 23 2014
78
- - Rebuild corrupt binaries
73
+ 3.1.1 Jul 10 2013
74
+ - Remove support for Ruby 1.8 in compiled win32 binaries
79
75
 
80
- 3.1.10 Jan 28 2015
81
- - Fix issue with dumping a BCrypt::Password instance to YAML in Ruby 2.2 [GH #107 by @mattwildig]
76
+ 3.1.0 May 07 2013
77
+ - Add BCrypt::Password.valid_hash?(str) to check if a string is a valid bcrypt password hash
78
+ - BCrypt::Password cost should be set to DEFAULT_COST if nil
79
+ - Add BCrypt::Engine.cost attribute for getting/setting a default cost externally
82
80
 
83
- 3.1.11 Mar 06 2016
84
- - Add support for Ruby 2.2 in compiled Windows binaries
81
+ 3.0.1 Sep 12 2011
82
+ - create raises an exception if the cost is higher than 31. GH #27
85
83
 
86
- 3.1.12 May 16 2018
87
- - Add support for Ruby 2.3, 2.4, and 2.5 in compiled Windows binaries
88
- - Fix compatibility with libxcrypt [GH #164 by @besser82]
84
+ 3.0.0 Aug 24 2011
85
+ - Bcrypt C implementation replaced with a public domain implementation.
86
+ - License changed to MIT
89
87
 
90
- [DRAFT] 4.0.0 MMM DD YYYY
91
- - No longer include compiled binaries for Windows. See GH #173.
92
- - Update C and Java implementations to latest versions [GH #182 by @fonica]
93
- - Bump default cost to 12 [GH #181 by @bdewater]
94
- - Remove explicit support for Rubies 1.8 and 1.9
88
+ 2.1.2 Sep 16 2009
89
+ - Fixed support for Solaris, OpenSolaris.
90
+
91
+ 2.1.1 Aug 14 2009
92
+ - JVM 1.4/1.5 compatibility [Hongli Lai]
93
+
94
+ 2.1.0 Aug 12 2009
95
+ - Improved code coverage, unit tests, and build chain. [Hongli Lai]
96
+ - Ruby 1.9 compatibility fixes. [Hongli Lai]
97
+ - JRuby support, using Damien Miller's jBCrypt. [Hongli Lai]
98
+ - Ruby 1.9 GIL releasing for high-cost hashes. [Hongli Lai]
99
+
100
+ 2.0.5 Mar 11 2009
101
+ - Fixed Ruby 1.8.5 compatibility. [Mike Pomraning]
102
+
103
+ 2.0.4 Mar 09 2009
104
+ - Added Ruby 1.9 compatibility. [Genki Takiuchi]
105
+ - Fixed segfaults on some different types of empty strings. [Mike Pomraning]
106
+
107
+ 2.0.3 May 07 2008
108
+ - Made exception classes descend from StandardError, not Exception [Dan42]
109
+ - Changed BCrypt::Engine.hash to BCrypt::Engine.hash_secret to avoid Merb
110
+ sorting issues. [Lee Pope]
111
+
112
+ 2.0.2 Jun 06 2007
113
+ - Fixed example code in the README [Winson]
114
+ - Fixed Solaris compatibility [Jeremy LaTrasse, Twitter crew]
115
+
116
+ 2.0.1 Mar 09 2007
117
+ - Fixed load path issues
118
+ - Fixed crashes when hashing weird values (e.g., false, etc.)
119
+
120
+ 2.0.0 Mar 07 2007
121
+ - Removed BCrypt::Password#exactly_equals -- use BCrypt::Password#eql? instead.
122
+ - Added BCrypt::Password#is_password?.
123
+ - Refactored out BCrypt::Internals into more useful BCrypt::Engine.
124
+ - Added validation of secrets -- nil is not healthy.
125
+
126
+ 1.0.0 Feb 27 2007
127
+ - Initial release.
data/README.md CHANGED
@@ -2,11 +2,9 @@
2
2
 
3
3
  An easy way to keep your users' passwords secure.
4
4
 
5
- * https://github.com/codahale/bcrypt-ruby/tree/master
6
-
7
- [![Travis Build Status](https://travis-ci.org/codahale/bcrypt-ruby.svg?branch=master)](https://travis-ci.org/codahale/bcrypt-ruby)
8
- [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/6fplerx9lnaf0hyo?svg=true)](https://ci.appveyor.com/project/TJSchuck35975/bcrypt-ruby)
5
+ * https://github.com/bcrypt-ruby/bcrypt-ruby/tree/master
9
6
 
7
+ [![Github Actions Build Status](https://github.com/bcrypt-ruby/bcrypt-ruby/actions/workflows/ruby.yml/badge.svg?branch=master)](https://github.com/bcrypt-ruby/bcrypt-ruby/actions/workflows/ruby.yml)
10
8
 
11
9
  ## Why you should use `bcrypt()`
12
10
 
@@ -32,8 +30,8 @@ re-hash those passwords. This vulnerability only affected the JRuby gem.
32
30
  The bcrypt gem is available on the following Ruby platforms:
33
31
 
34
32
  * JRuby
35
- * RubyInstaller 2.0 – 2.5 builds on Windows with the DevKit
36
- * Any 2.0 – 2.5 Ruby on a BSD/OS X/Linux system with a compiler
33
+ * RubyInstaller builds on Windows with the DevKit
34
+ * Any modern Ruby on a BSD/OS X/Linux system with a compiler
37
35
 
38
36
  ## How to use `bcrypt()` in your Rails application
39
37
 
@@ -688,20 +688,21 @@ public class BCrypt {
688
688
  */
689
689
  private byte[] crypt_raw(byte password[], byte salt[], int log_rounds,
690
690
  boolean sign_ext_bug, int safety) {
691
- int rounds, i, j;
691
+ long rounds;
692
+ int i, j;
692
693
  int cdata[] = bf_crypt_ciphertext.clone();
693
694
  int clen = cdata.length;
694
695
  byte ret[];
695
696
 
696
697
  if (log_rounds < 4 || log_rounds > 31)
697
698
  throw new IllegalArgumentException ("Bad number of rounds");
698
- rounds = 1 << log_rounds;
699
+ rounds = roundsForLogRounds(log_rounds);
699
700
  if (salt.length != BCRYPT_SALT_LEN)
700
701
  throw new IllegalArgumentException ("Bad salt length");
701
702
 
702
703
  init_key();
703
704
  ekskey(salt, password, sign_ext_bug, safety);
704
- for (i = 0; i < rounds; i++) {
705
+ for (long r = 0; r < rounds; r++) {
705
706
  key(password, sign_ext_bug, safety);
706
707
  key(salt, false, safety);
707
708
  }
data/ext/mri/bcrypt_ext.c CHANGED
@@ -1,59 +1,120 @@
1
1
  #include <ruby.h>
2
2
  #include <ow-crypt.h>
3
3
 
4
+ #ifdef HAVE_RUBY_THREAD_H
5
+ #include <ruby/thread.h>
6
+ #endif
7
+
4
8
  static VALUE mBCrypt;
5
9
  static VALUE cBCryptEngine;
6
10
 
11
+ struct bc_salt_args {
12
+ const char * prefix;
13
+ unsigned long count;
14
+ const char * input;
15
+ int size;
16
+ };
17
+
18
+ static void * bc_salt_nogvl(void * ptr) {
19
+ struct bc_salt_args * args = ptr;
20
+
21
+ return crypt_gensalt_ra(args->prefix, args->count, args->input, args->size);
22
+ }
23
+
7
24
  /* Given a logarithmic cost parameter, generates a salt for use with +bc_crypt+.
8
25
  */
9
26
  static VALUE bc_salt(VALUE self, VALUE prefix, VALUE count, VALUE input) {
10
27
  char * salt;
11
28
  VALUE str_salt;
12
-
13
- salt = crypt_gensalt_ra(
14
- StringValuePtr(prefix),
15
- NUM2ULONG(count),
16
- NIL_P(input) ? NULL : StringValuePtr(input),
17
- NIL_P(input) ? 0 : RSTRING_LEN(input));
29
+ struct bc_salt_args args;
30
+
31
+ /* duplicate the parameters for thread safety. If another thread has a
32
+ * reference to the parameters and mutates them while we are working,
33
+ * that would be very bad. Duping the strings means that the reference
34
+ * isn't shared. */
35
+ prefix = rb_str_new_frozen(prefix);
36
+ input = rb_str_new_frozen(input);
37
+
38
+ args.prefix = StringValueCStr(prefix);
39
+ args.count = NUM2ULONG(count);
40
+ args.input = NIL_P(input) ? NULL : StringValuePtr(input);
41
+ args.size = NIL_P(input) ? 0 : RSTRING_LEN(input);
42
+
43
+ #ifdef HAVE_RUBY_THREAD_H
44
+ salt = rb_thread_call_without_gvl(bc_salt_nogvl, &args, NULL, NULL);
45
+ #else
46
+ salt = bc_salt_nogvl((void *)&args);
47
+ #endif
18
48
 
19
49
  if(!salt) return Qnil;
20
50
 
21
51
  str_salt = rb_str_new2(salt);
22
- xfree(salt);
52
+
53
+ RB_GC_GUARD(prefix);
54
+ RB_GC_GUARD(input);
55
+ free(salt);
23
56
 
24
57
  return str_salt;
25
58
  }
26
59
 
60
+ struct bc_crypt_args {
61
+ const char * key;
62
+ const char * setting;
63
+ void * data;
64
+ int size;
65
+ };
66
+
67
+ static void * bc_crypt_nogvl(void * ptr) {
68
+ struct bc_crypt_args * args = ptr;
69
+
70
+ return crypt_ra(args->key, args->setting, &args->data, &args->size);
71
+ }
72
+
27
73
  /* Given a secret and a salt, generates a salted hash (which you can then store safely).
28
74
  */
29
75
  static VALUE bc_crypt(VALUE self, VALUE key, VALUE setting) {
30
76
  char * value;
31
- void * data;
32
- int size;
33
77
  VALUE out;
34
78
 
35
- data = NULL;
36
- size = 0xDEADBEEF;
79
+ struct bc_crypt_args args;
37
80
 
38
81
  if(NIL_P(key) || NIL_P(setting)) return Qnil;
39
82
 
40
- value = crypt_ra(
41
- NIL_P(key) ? NULL : StringValuePtr(key),
42
- NIL_P(setting) ? NULL : StringValuePtr(setting),
43
- &data,
44
- &size);
83
+ /* duplicate the parameters for thread safety. If another thread has a
84
+ * reference to the parameters and mutates them while we are working,
85
+ * that would be very bad. Duping the strings means that the reference
86
+ * isn't shared. */
87
+ key = rb_str_new_frozen(key);
88
+ setting = rb_str_new_frozen(setting);
89
+
90
+ args.data = NULL;
91
+ args.size = 0xDEADBEEF;
92
+ args.key = NIL_P(key) ? NULL : StringValueCStr(key);
93
+ args.setting = NIL_P(setting) ? NULL : StringValueCStr(setting);
94
+
95
+ #ifdef HAVE_RUBY_THREAD_H
96
+ value = rb_thread_call_without_gvl(bc_crypt_nogvl, &args, NULL, NULL);
97
+ #else
98
+ value = bc_crypt_nogvl((void *)&args);
99
+ #endif
45
100
 
46
- if(!value) return Qnil;
101
+ if(!value || !args.data) return Qnil;
47
102
 
48
103
  out = rb_str_new2(value);
49
104
 
50
- xfree(data);
105
+ RB_GC_GUARD(key);
106
+ RB_GC_GUARD(setting);
107
+ free(args.data);
51
108
 
52
109
  return out;
53
110
  }
54
111
 
55
112
  /* Create the BCrypt and BCrypt::Engine modules, and populate them with methods. */
56
113
  void Init_bcrypt_ext(){
114
+ #ifdef HAVE_RB_EXT_RACTOR_SAFE
115
+ rb_ext_ractor_safe(true);
116
+ #endif
117
+
57
118
  mBCrypt = rb_define_module("BCrypt");
58
119
  cBCryptEngine = rb_define_class_under(mBCrypt, "Engine", rb_cObject);
59
120
 
@@ -361,7 +361,7 @@ static BF_ctx BF_init_state = {
361
361
  }
362
362
  };
363
363
 
364
- static unsigned char BF_itoa64[64 + 1] =
364
+ static const unsigned char BF_itoa64[64 + 1] =
365
365
  "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
366
366
 
367
367
  static unsigned char BF_atoi64[0x60] = {
@@ -387,9 +387,8 @@ static int BF_decode(BF_word *dst, const char *src, int size)
387
387
  unsigned char *dptr = (unsigned char *)dst;
388
388
  unsigned char *end = dptr + size;
389
389
  const unsigned char *sptr = (const unsigned char *)src;
390
- unsigned int tmp, c1, c2, c3, c4;
391
-
392
390
  do {
391
+ unsigned int tmp, c1, c2, c3, c4;
393
392
  BF_safe_atoi64(c1, *sptr++);
394
393
  BF_safe_atoi64(c2, *sptr++);
395
394
  *dptr++ = (c1 << 2) | ((c2 & 0x30) >> 4);
@@ -402,7 +401,6 @@ static int BF_decode(BF_word *dst, const char *src, int size)
402
401
  BF_safe_atoi64(c4, *sptr++);
403
402
  *dptr++ = ((c3 & 0x03) << 6) | c4;
404
403
  } while (dptr < end);
405
-
406
404
  return 0;
407
405
  }
408
406
 
@@ -411,9 +409,8 @@ static void BF_encode(char *dst, const BF_word *src, int size)
411
409
  const unsigned char *sptr = (const unsigned char *)src;
412
410
  const unsigned char *end = sptr + size;
413
411
  unsigned char *dptr = (unsigned char *)dst;
414
- unsigned int c1, c2;
415
-
416
412
  do {
413
+ unsigned int c1, c2;
417
414
  c1 = *sptr++;
418
415
  *dptr++ = BF_itoa64[c1 >> 2];
419
416
  c1 = (c1 & 0x03) << 4;
@@ -442,10 +439,9 @@ static void BF_swap(BF_word *x, int count)
442
439
  {
443
440
  static int endianness_check = 1;
444
441
  char *is_little_endian = (char *)&endianness_check;
445
- BF_word tmp;
446
-
447
442
  if (*is_little_endian)
448
443
  do {
444
+ BF_word tmp;
449
445
  tmp = *x;
450
446
  tmp = (tmp << 16) | (tmp >> 16);
451
447
  *x++ = ((tmp & 0x00FF00FF) << 8) | ((tmp >> 8) & 0x00FF00FF);
@@ -517,7 +513,7 @@ static void BF_swap(BF_word *x, int count)
517
513
  R = L; \
518
514
  L = tmp4 ^ data.ctx.P[BF_N + 1];
519
515
 
520
- #if BF_ASM
516
+ #if BF_ASM == 1
521
517
  #define BF_body() \
522
518
  _BF_body_r(&data.ctx);
523
519
  #else
@@ -650,7 +646,7 @@ static char *BF_crypt(const char *key, const char *setting,
650
646
  char *output, int size,
651
647
  BF_word min)
652
648
  {
653
- #if BF_ASM
649
+ #if BF_ASM == 1
654
650
  extern void _BF_body_r(BF_ctx *ctx);
655
651
  #endif
656
652
  struct {
@@ -28,7 +28,7 @@
28
28
  /* Just to make sure the prototypes match the actual definitions */
29
29
  #include "crypt_gensalt.h"
30
30
 
31
- unsigned char _crypt_itoa64[64 + 1] =
31
+ const unsigned char _crypt_itoa64[64 + 1] =
32
32
  "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
33
33
 
34
34
  char *_crypt_gensalt_traditional_rn(const char *prefix, unsigned long count,
@@ -17,7 +17,7 @@
17
17
  #ifndef _CRYPT_GENSALT_H
18
18
  #define _CRYPT_GENSALT_H
19
19
 
20
- extern unsigned char _crypt_itoa64[];
20
+ extern const unsigned char _crypt_itoa64[];
21
21
  extern char *_crypt_gensalt_traditional_rn(const char *prefix,
22
22
  unsigned long count,
23
23
  const char *input, int size, char *output, int output_size);
data/ext/mri/wrapper.c CHANGED
@@ -17,6 +17,9 @@
17
17
  #include <stdlib.h>
18
18
  #include <string.h>
19
19
 
20
+ /* Redefine strdup to ruby_strdup in case string.h doesn't export it. */
21
+ #include <ruby/util.h>
22
+
20
23
  #include <errno.h>
21
24
  #ifndef __set_errno
22
25
  #define __set_errno(val) errno = (val)
@@ -176,7 +179,7 @@ char *crypt_ra(const char *key, const char *setting,
176
179
  return _crypt_blowfish_rn(key, setting, (char *)*data, *size);
177
180
  }
178
181
 
179
- char *crypt_r(const char *key, const char *setting, void *data)
182
+ char *crypt_r(const char *key, const char *setting, struct crypt_data *data)
180
183
  {
181
184
  return _crypt_retval_magic(
182
185
  crypt_rn(key, setting, data, CRYPT_OUTPUT_SIZE),
data/ext/mri/x86.S CHANGED
@@ -199,5 +199,5 @@ BF_die:
199
199
  #endif
200
200
 
201
201
  #if defined(__ELF__) && defined(__linux__)
202
- .section .note.GNU-stack,"",@progbits
202
+ .section .note.GNU-stack,"",%progbits
203
203
  #endif
data/lib/bcrypt/engine.rb CHANGED
@@ -5,6 +5,16 @@ module BCrypt
5
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
 
@@ -41,14 +51,23 @@ 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)
56
+ unless _.nil?
57
+ warn "[DEPRECATION] Passing the third argument to " \
58
+ "`BCrypt::Engine.hash_secret` is deprecated. " \
59
+ "Please do not pass the third argument which " \
60
+ "is currently not used."
61
+ end
62
+
46
63
  if valid_secret?(secret)
47
64
  if valid_salt?(salt)
48
65
  if RUBY_PLATFORM == "java"
49
66
  Java.bcrypt_jruby.BCrypt.hashpw(secret.to_s.to_java_bytes, salt.to_s)
50
67
  else
51
- __bc_crypt(secret.to_s, salt)
68
+ secret = secret.to_s
69
+ secret = secret.byteslice(0, MAX_SECRET_BYTESIZE) if secret && secret.bytesize > MAX_SECRET_BYTESIZE
70
+ __bc_crypt(secret, salt)
52
71
  end
53
72
  else
54
73
  raise Errors::InvalidSalt.new("invalid salt")
@@ -68,8 +87,7 @@ module BCrypt
68
87
  if RUBY_PLATFORM == "java"
69
88
  Java.bcrypt_jruby.BCrypt.gensalt(cost)
70
89
  else
71
- prefix = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"
72
- __bc_salt(prefix, cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH))
90
+ __bc_salt("$2a$", cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH))
73
91
  end
74
92
  else
75
93
  raise Errors::InvalidCost.new("cost must be numeric and > 0")
@@ -78,7 +96,7 @@ module BCrypt
78
96
 
79
97
  # Returns true if +salt+ is a valid bcrypt() salt, false if not.
80
98
  def self.valid_salt?(salt)
81
- !!(salt =~ /^\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}$/)
99
+ !!(salt =~ /\A\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}\z/)
82
100
  end
83
101
 
84
102
  # Returns true if +secret+ is a valid bcrypt() secret, false if not.
@@ -99,7 +117,7 @@ module BCrypt
99
117
  # # should take less than 1000ms
100
118
  # BCrypt::Password.create("woo", :cost => 12)
101
119
  def self.calibrate(upper_time_limit_in_ms)
102
- 40.times do |i|
120
+ (BCrypt::Engine::MIN_COST..BCrypt::Engine::MAX_COST-1).each do |i|
103
121
  start_time = Time.now
104
122
  Password.create("testing testing", :cost => i+1)
105
123
  end_time = Time.now - start_time
@@ -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,8 +62,28 @@ 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
76
+ #
77
+ # secret == @password # => probably False, because the secret is not a BCrypt::Password instance.
65
78
  def ==(secret)
66
- super(BCrypt::Engine.hash_secret(secret, @salt))
79
+ hash = BCrypt::Engine.hash_secret(secret, @salt)
80
+
81
+ return false if hash.strip.empty? || strip.empty? || hash.bytesize != bytesize
82
+
83
+ # Constant time comparison so they can't tell the length.
84
+ res = 0
85
+ bytesize.times { |i| res |= getbyte(i) ^ hash.getbyte(i) }
86
+ res == 0
67
87
  end
68
88
  alias_method :is_password?, :==
69
89
 
@@ -83,5 +103,4 @@ module BCrypt
83
103
  return v.to_str, c.to_i, h[0, 29].to_str, mash[-31, 31].to_str
84
104
  end
85
105
  end
86
-
87
106
  end