From c6293771dcad7a19acafa9cfafe195789a2e08c5 Mon Sep 17 00:00:00 2001 From: Ben Somers Date: Mon, 17 Sep 2012 13:55:17 -0700 Subject: [PATCH 1/5] Correcting out-of-date comment --- lib/authlogic/crypto_providers/sha256.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/authlogic/crypto_providers/sha256.rb b/lib/authlogic/crypto_providers/sha256.rb index 8bc4077a..4939aa85 100644 --- a/lib/authlogic/crypto_providers/sha256.rb +++ b/lib/authlogic/crypto_providers/sha256.rb @@ -27,7 +27,7 @@ class Sha256 class << self attr_accessor :join_token - # The number of times to loop through the encryption. This is ten because that is what restful_authentication defaults to. + # The number of times to loop through the encryption. def stretches @stretches ||= 20 end From 09d4569cd2252308078d6dc50c4b723ae567f6f0 Mon Sep 17 00:00:00 2001 From: Ben Somers Date: Mon, 17 Sep 2012 13:55:25 -0700 Subject: [PATCH 2/5] Fixing typo in test name --- test/crypto_provider_test/bcrypt_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/crypto_provider_test/bcrypt_test.rb b/test/crypto_provider_test/bcrypt_test.rb index 5e44f269..321a99f2 100644 --- a/test/crypto_provider_test/bcrypt_test.rb +++ b/test/crypto_provider_test/bcrypt_test.rb @@ -1,7 +1,7 @@ require 'test_helper' module CryptoProviderTest - class BCrpytTest < ActiveSupport::TestCase + class BCryptTest < ActiveSupport::TestCase def test_encrypt assert Authlogic::CryptoProviders::BCrypt.encrypt("mypass") end @@ -11,4 +11,4 @@ def test_matches assert Authlogic::CryptoProviders::BCrypt.matches?(hash, "mypass") end end -end \ No newline at end of file +end From 1f7714822150e60c0055f5bbc4c51c5fb45fa27b Mon Sep 17 00:00:00 2001 From: Ben Somers Date: Mon, 17 Sep 2012 16:14:26 -0700 Subject: [PATCH 3/5] Just cleaning up a few typos And maybe adding one small editorial comment about SHA1 :) --- lib/authlogic/acts_as_authentic/password.rb | 2 +- lib/authlogic/crypto_providers/aes256.rb | 4 ++-- lib/authlogic/crypto_providers/bcrypt.rb | 4 ++-- lib/authlogic/crypto_providers/sha1.rb | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/authlogic/acts_as_authentic/password.rb b/lib/authlogic/acts_as_authentic/password.rb index 9fee8c02..f39b208b 100644 --- a/lib/authlogic/acts_as_authentic/password.rb +++ b/lib/authlogic/acts_as_authentic/password.rb @@ -306,7 +306,7 @@ def encrypt_arguments(raw_password, check_against_database, arguments_type = nil # If the index > 0 then we are using an "transition from" crypto provider. # If the encryptor has a cost and the cost it outdated. # If we aren't using database values - # If we are using database values, only if the password hasnt change so we don't overwrite any changes + # If we are using database values, only if the password hasn't changed so we don't overwrite any changes def transition_password?(index, encryptor, crypted, check_against_database) (index > 0 || (encryptor.respond_to?(:cost_matches?) && !encryptor.cost_matches?(send(crypted_password_field)))) && (!check_against_database || !send("#{crypted_password_field}_changed?")) diff --git a/lib/authlogic/crypto_providers/aes256.rb b/lib/authlogic/crypto_providers/aes256.rb index 40e79709..fc383fa1 100644 --- a/lib/authlogic/crypto_providers/aes256.rb +++ b/lib/authlogic/crypto_providers/aes256.rb @@ -7,7 +7,7 @@ module CryptoProviders # # Authlogic::CryptoProviders::AES256.key = "my really long and unique key, preferrably a bunch of random characters" # - # My final comment is that this is a strong encryption method, but its main weakness is that its reversible. If you do not need to reverse the hash + # My final comment is that this is a strong encryption method, but its main weakness is that it's reversible. If you do not need to reverse the hash # then you should consider Sha512 or BCrypt instead. # # Keep your key in a safe place, some even say the key should be stored on a separate server. @@ -40,4 +40,4 @@ def aes end end end -end \ No newline at end of file +end diff --git a/lib/authlogic/crypto_providers/bcrypt.rb b/lib/authlogic/crypto_providers/bcrypt.rb index f2905054..6450fa32 100644 --- a/lib/authlogic/crypto_providers/bcrypt.rb +++ b/lib/authlogic/crypto_providers/bcrypt.rb @@ -30,7 +30,7 @@ module CryptoProviders # # You can play around with the cost to get that perfect balance between performance and security. # - # Decided BCrypt is for you? Just insall the bcrypt gem: + # Decided BCrypt is for you? Just install the bcrypt gem: # # gem install bcrypt-ruby # @@ -87,4 +87,4 @@ def new_from_hash(hash) end end end -end \ No newline at end of file +end diff --git a/lib/authlogic/crypto_providers/sha1.rb b/lib/authlogic/crypto_providers/sha1.rb index 819b874a..1dc79f68 100644 --- a/lib/authlogic/crypto_providers/sha1.rb +++ b/lib/authlogic/crypto_providers/sha1.rb @@ -3,7 +3,7 @@ module Authlogic module CryptoProviders # This class was made for the users transitioning from restful_authentication. I highly discourage using this - # crypto provider as it inferior to your other options. Please use any other provider offered by Authlogic. + # crypto provider as it is far inferior to your other options. Please use any other provider offered by Authlogic. class Sha1 class << self def join_token @@ -32,4 +32,4 @@ def matches?(crypted, *tokens) end end end -end \ No newline at end of file +end From d60f81bbd6565e879db5f6c286147bf46c514dcd Mon Sep 17 00:00:00 2001 From: Ben Somers Date: Mon, 17 Sep 2012 16:16:11 -0700 Subject: [PATCH 4/5] Adding SCrypt support Modeled very closely on BCrypt support. Unfortunately, there is no simple way to detect tuning changes (like BCrypt does with its cost parameter) with the current ruby implementation of SCrypt. --- Gemfile.lock | 2 + authlogic.gemspec | 3 +- lib/authlogic.rb | 1 + lib/authlogic/crypto_providers/scrypt.rb | 80 ++++++++++++++++++++++++ test/crypto_provider_test/scrypt_test.rb | 14 +++++ 5 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 lib/authlogic/crypto_providers/scrypt.rb create mode 100644 test/crypto_provider_test/scrypt_test.rb diff --git a/Gemfile.lock b/Gemfile.lock index 59656e45..335070c8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -25,6 +25,7 @@ GEM i18n (0.6.0) multi_json (1.3.6) rake (0.9.2.2) + scrypt (1.1.0) sqlite3 (1.3.6) tzinfo (0.3.33) @@ -35,4 +36,5 @@ DEPENDENCIES authlogic! bcrypt-ruby rake + scrypt sqlite3 diff --git a/authlogic.gemspec b/authlogic.gemspec index 714e9526..9fe4ddde 100644 --- a/authlogic.gemspec +++ b/authlogic.gemspec @@ -15,10 +15,11 @@ Gem::Specification.new do |s| s.add_dependency 'activesupport', '>= 3.0.0' s.add_development_dependency 'rake' s.add_development_dependency 'bcrypt-ruby' + s.add_development_dependency 'scrypt' s.add_development_dependency 'sqlite3' s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] -end \ No newline at end of file +end diff --git a/lib/authlogic.rb b/lib/authlogic.rb index 3efd3d63..884264ac 100644 --- a/lib/authlogic.rb +++ b/lib/authlogic.rb @@ -15,6 +15,7 @@ "crypto_providers/sha512", "crypto_providers/bcrypt", "crypto_providers/aes256", + "crypto_providers/scrypt", "authenticates_many/base", "authenticates_many/association", diff --git a/lib/authlogic/crypto_providers/scrypt.rb b/lib/authlogic/crypto_providers/scrypt.rb new file mode 100644 index 00000000..035b7d1f --- /dev/null +++ b/lib/authlogic/crypto_providers/scrypt.rb @@ -0,0 +1,80 @@ +begin + require "scrypt" +rescue LoadError + "sudo gem install scrypt" +end + +module Authlogic + module CryptoProviders + # If you want a stronger hashing algorithm, but would prefer not to use BCrypt, SCrypt is another option. + # SCrypt is newer and less popular (and so less-tested), but it's designed specifically to avoid a theoretical + # hardware attack against BCrypt. Just as with BCrypt, you are sacrificing performance relative to SHA2 algorithms, + # but the increased security may well be worth it. (That performance sacrifice is the exact reason it's much, much + # harder for an attacker to brute-force your paswords). + # Decided SCrypt is for you? Just install the bcrypt gem: + # + # gem install scrypt + # + # Tell acts_as_authentic to use it: + # + # acts_as_authentic do |c| + # c.crypto_provider = Authlogic::CryptoProviders::SCrypt + # end + class SCrypt + class << self + DEFAULTS = {:key_len => 32, :salt_size => 8, :max_time => 0.2, :max_mem => 1024 * 1024, :max_memfrac => 0.5} + + attr_writer :key_len, :salt_size, :max_time, :max_mem, :max_memfrac + # Key length - length in bytes of generated key, from 16 to 512. + def key_len + @key_len ||= DEFAULTS[:key_len] + end + + # Salt size - size in bytes of random salt, from 8 to 32 + def salt_size + @salt_size ||= DEFAULTS[:salt_size] + end + + # Max time - maximum time spent in computation + def max_time + @max_time ||= DEFAULTS[:max_time] + end + + # Max memory - maximum memory usage. The minimum is always 1MB + def max_mem + @max_mem ||= DEFAULTS[:max_mem] + end + + # Max memory fraction - maximum memory out of all available. Always greater than zero and <= 0.5. + def max_memfrac + @max_memfrac ||= DEFAULTS[:max_memfrac] + end + + # Creates an SCrypt hash for the password passed. + def encrypt(*tokens) + ::SCrypt::Password.create(join_tokens(tokens), :key_len => key_len, :salt_size => salt_size, :max_mem => max_mem, :max_memfrac => max_memfrac, :max_time => max_time) + end + + # Does the hash match the tokens? Uses the same tokens that were used to encrypt. + def matches?(hash, *tokens) + hash = new_from_hash(hash) + return false if hash.blank? + hash == join_tokens(tokens) + end + + private + def join_tokens(tokens) + tokens.flatten.join + end + + def new_from_hash(hash) + begin + ::SCrypt::Password.new(hash) + rescue ::SCrypt::Errors::InvalidHash + return nil + end + end + end + end + end +end diff --git a/test/crypto_provider_test/scrypt_test.rb b/test/crypto_provider_test/scrypt_test.rb new file mode 100644 index 00000000..5608e948 --- /dev/null +++ b/test/crypto_provider_test/scrypt_test.rb @@ -0,0 +1,14 @@ +require 'test_helper' + +module CryptoProviderTest + class SCryptTest < ActiveSupport::TestCase + def test_encrypt + assert Authlogic::CryptoProviders::SCrypt.encrypt("mypass") + end + + def test_matches + hash = Authlogic::CryptoProviders::SCrypt.encrypt("mypass") + assert Authlogic::CryptoProviders::SCrypt.matches?(hash, "mypass") + end + end +end From a889f9a3f3611ece7a764edc02fb86e83e32d208 Mon Sep 17 00:00:00 2001 From: Sergii Boiko Date: Mon, 26 Nov 2012 17:37:04 +0200 Subject: [PATCH 5/5] Rollback to LOWER comparison instead of (I)LIKE because of bug and inability to use index with ILIKE for Postgres --- lib/authlogic/acts_as_authentic/login.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/authlogic/acts_as_authentic/login.rb b/lib/authlogic/acts_as_authentic/login.rb index 7ea87a13..d70ca06a 100644 --- a/lib/authlogic/acts_as_authentic/login.rb +++ b/lib/authlogic/acts_as_authentic/login.rb @@ -94,7 +94,7 @@ def merge_validates_uniqueness_of_login_field_options(options = {}) # manner that they handle that. If you are using the login field and set false for the :case_sensitive option in # validates_uniqueness_of_login_field_options this method will modify the query to look something like: # - # where("#{quoted_table_name}.#{field} LIKE ?", login).first + # where("LOWER(#{quoted_table_name}.#{login_field}) = ?", login.downcase).first # # If you don't specify this it calls the good old find_by_* method: # @@ -118,8 +118,7 @@ def find_with_case(field, value, sensitivity = true) if sensitivity send("find_by_#{field}", value) else - like_word = ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" ? "ILIKE" : "LIKE" - where("#{quoted_table_name}.#{field} #{like_word} ?", value.mb_chars).first + where("LOWER(#{quoted_table_name}.#{field}) = ?", value.mb_chars.downcase).first end end end