Permalink
Browse files

Merge pull request #330 from bensomers/scrypt

SCrypt support
  • Loading branch information...
2 parents d8c61db + d60f81b commit 2646d1221dfb699e473fffccf17a07c3065ae5bf @binarylogic committed Dec 7, 2012
View
@@ -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
View
@@ -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
+end
View
@@ -15,6 +15,7 @@
"crypto_providers/sha512",
"crypto_providers/bcrypt",
"crypto_providers/aes256",
+ "crypto_providers/scrypt",
"authenticates_many/base",
"authenticates_many/association",
@@ -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?"))
@@ -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
+end
@@ -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
+end
@@ -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
@@ -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
+end
@@ -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
@@ -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
+end
@@ -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

0 comments on commit 2646d12

Please sign in to comment.