From 9fe80a5b2af9f540178b9440fcc7bdc25ea20bb8 Mon Sep 17 00:00:00 2001 From: Copywright Date: Sun, 24 Jan 2016 12:49:00 -0500 Subject: [PATCH 1/7] Add argon2 password hasing feature --- .gems | 1 + lib/shield.rb | 30 ++++++++++++++++++++++++------ shield.gemspec | 1 + test/password.rb | 9 ++++++++- 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/.gems b/.gems index 4bc6d8a..f669e3e 100644 --- a/.gems +++ b/.gems @@ -2,3 +2,4 @@ cutest -v 1.2.1 rack-test -v 0.6.2 cuba -v 3.1.0 armor -v 0.0.3 +argon2 -v 0.1.4 diff --git a/lib/shield.rb b/lib/shield.rb index 47985e3..4814151 100644 --- a/lib/shield.rb +++ b/lib/shield.rb @@ -1,4 +1,5 @@ require "armor" +require "argon2" require "uri" module Shield @@ -98,6 +99,8 @@ def password=(password) end module Password + attr_reader :encryption_mode + Error = Class.new(StandardError) # == DOS attack fix @@ -108,18 +111,29 @@ module Password # @see: https://www.djangoproject.com/weblog/2013/sep/15/security/ MAX_LEN = 4096 - def self.encrypt(password, salt = generate_salt) - digest(password, salt) + salt + def self.encrypt(password, salt = generate_salt, mode: :armor) + @encryption_mode = mode + + case @encryption_mode + when :armor then digest_with_armor(password, salt) + salt + when :argon2 then digest_with_argon2(password) + end end def self.check(password, encrypted) - sha512, salt = encrypted.to_s[0...128], encrypted.to_s[128..-1] - - Armor.compare(digest(password, salt), sha512) + case @encryption_mode + when :armor + sha512, salt = encrypted.to_s[0...128], encrypted.to_s[128..-1] + Armor.compare(digest_with_armor(password, salt), sha512) + when :argon2 + Argon2::Password.verify_password(password, encrypted) + else + raise Error, ":armor and :argon2 are the only supported encryption methods at this time." + end end protected - def self.digest(password, salt) + def self.digest_with_armor(password, salt) raise Error if password.length > MAX_LEN Armor.digest(password, salt) @@ -128,5 +142,9 @@ def self.digest(password, salt) def self.generate_salt Armor.hex(OpenSSL::Random.random_bytes(32)) end + + def self.digest_with_argon2(password) + Argon2::Password.hash(password) + end end end diff --git a/shield.gemspec b/shield.gemspec index cb4f801..d1edaf1 100644 --- a/shield.gemspec +++ b/shield.gemspec @@ -15,6 +15,7 @@ Gem::Specification.new do |s| s.files = `git ls-files`.split("\n") s.add_dependency "armor" + s.add_dependency "argon2" s.add_development_dependency "cutest" s.add_development_dependency "rack-test" s.add_development_dependency "cuba" diff --git a/test/password.rb b/test/password.rb index 8dd92a1..4accdc2 100644 --- a/test/password.rb +++ b/test/password.rb @@ -1,11 +1,18 @@ +require "pry" require_relative "helper" scope do - test "encrypt" do + test "armor encryption" do encrypted = Shield::Password.encrypt("password") assert Shield::Password.check("password", encrypted) end + test "argon2 encryption" do + encrypted = Shield::Password.encrypt("password", mode: :argon2) + assert encrypted.include? 'argon2' + assert Shield::Password.check("password", encrypted) + end + test "with custom 64 character salt" do encrypted = Shield::Password.encrypt("password", "A" * 64) assert Shield::Password.check("password", encrypted) From e02e072e092ca9cbfcfa4c9ebda51e93ae72e600 Mon Sep 17 00:00:00 2001 From: Copywright Date: Sun, 24 Jan 2016 12:49:00 -0500 Subject: [PATCH 2/7] Add argon2 password hashing feature --- .gems | 1 + lib/shield.rb | 30 ++++++++++++++++++++++++------ shield.gemspec | 1 + test/password.rb | 9 ++++++++- 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/.gems b/.gems index 4bc6d8a..f669e3e 100644 --- a/.gems +++ b/.gems @@ -2,3 +2,4 @@ cutest -v 1.2.1 rack-test -v 0.6.2 cuba -v 3.1.0 armor -v 0.0.3 +argon2 -v 0.1.4 diff --git a/lib/shield.rb b/lib/shield.rb index 47985e3..4814151 100644 --- a/lib/shield.rb +++ b/lib/shield.rb @@ -1,4 +1,5 @@ require "armor" +require "argon2" require "uri" module Shield @@ -98,6 +99,8 @@ def password=(password) end module Password + attr_reader :encryption_mode + Error = Class.new(StandardError) # == DOS attack fix @@ -108,18 +111,29 @@ module Password # @see: https://www.djangoproject.com/weblog/2013/sep/15/security/ MAX_LEN = 4096 - def self.encrypt(password, salt = generate_salt) - digest(password, salt) + salt + def self.encrypt(password, salt = generate_salt, mode: :armor) + @encryption_mode = mode + + case @encryption_mode + when :armor then digest_with_armor(password, salt) + salt + when :argon2 then digest_with_argon2(password) + end end def self.check(password, encrypted) - sha512, salt = encrypted.to_s[0...128], encrypted.to_s[128..-1] - - Armor.compare(digest(password, salt), sha512) + case @encryption_mode + when :armor + sha512, salt = encrypted.to_s[0...128], encrypted.to_s[128..-1] + Armor.compare(digest_with_armor(password, salt), sha512) + when :argon2 + Argon2::Password.verify_password(password, encrypted) + else + raise Error, ":armor and :argon2 are the only supported encryption methods at this time." + end end protected - def self.digest(password, salt) + def self.digest_with_armor(password, salt) raise Error if password.length > MAX_LEN Armor.digest(password, salt) @@ -128,5 +142,9 @@ def self.digest(password, salt) def self.generate_salt Armor.hex(OpenSSL::Random.random_bytes(32)) end + + def self.digest_with_argon2(password) + Argon2::Password.hash(password) + end end end diff --git a/shield.gemspec b/shield.gemspec index cb4f801..d1edaf1 100644 --- a/shield.gemspec +++ b/shield.gemspec @@ -15,6 +15,7 @@ Gem::Specification.new do |s| s.files = `git ls-files`.split("\n") s.add_dependency "armor" + s.add_dependency "argon2" s.add_development_dependency "cutest" s.add_development_dependency "rack-test" s.add_development_dependency "cuba" diff --git a/test/password.rb b/test/password.rb index 8dd92a1..4accdc2 100644 --- a/test/password.rb +++ b/test/password.rb @@ -1,11 +1,18 @@ +require "pry" require_relative "helper" scope do - test "encrypt" do + test "armor encryption" do encrypted = Shield::Password.encrypt("password") assert Shield::Password.check("password", encrypted) end + test "argon2 encryption" do + encrypted = Shield::Password.encrypt("password", mode: :argon2) + assert encrypted.include? 'argon2' + assert Shield::Password.check("password", encrypted) + end + test "with custom 64 character salt" do encrypted = Shield::Password.encrypt("password", "A" * 64) assert Shield::Password.check("password", encrypted) From aa00dccd27cbee3eae8d3efb8ab8fd5d784e20ee Mon Sep 17 00:00:00 2001 From: Copywright Date: Sun, 24 Jan 2016 16:14:12 -0500 Subject: [PATCH 3/7] remove pry requirement --- test/password.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/password.rb b/test/password.rb index 4accdc2..2be2414 100644 --- a/test/password.rb +++ b/test/password.rb @@ -1,4 +1,3 @@ -require "pry" require_relative "helper" scope do From 2164b1d006ef23c70825190aba34543152c52339 Mon Sep 17 00:00:00 2001 From: Herbert Joseph Date: Mon, 25 Jan 2016 23:28:59 -0500 Subject: [PATCH 4/7] Set Encryption Mode in Shield::Model --- lib/shield.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/shield.rb b/lib/shield.rb index 4814151..08a0ad3 100644 --- a/lib/shield.rb +++ b/lib/shield.rb @@ -93,8 +93,8 @@ def is_valid_password?(user, password) end end - def password=(password) - self.crypted_password = Shield::Password.encrypt(password.to_s) + def password=(password, mode: :argon2) + self.crypted_password = Shield::Password.encrypt(password.to_s, mode) end end From 1b6a65d6a662031aad8157d216dbe44b95f533ee Mon Sep 17 00:00:00 2001 From: Herbert Joseph Date: Mon, 25 Jan 2016 23:31:39 -0500 Subject: [PATCH 5/7] Fix setting --- lib/shield.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/shield.rb b/lib/shield.rb index 08a0ad3..c8018c1 100644 --- a/lib/shield.rb +++ b/lib/shield.rb @@ -93,8 +93,8 @@ def is_valid_password?(user, password) end end - def password=(password, mode: :argon2) - self.crypted_password = Shield::Password.encrypt(password.to_s, mode) + def password=(password, mode: :armor) + self.crypted_password = Shield::Password.encrypt(password.to_s, mode: mode) end end From fd35750e200d7cdf638edd14fbb01f1e6edde565 Mon Sep 17 00:00:00 2001 From: Herbert Joseph Date: Mon, 25 Jan 2016 23:33:33 -0500 Subject: [PATCH 6/7] Default to argon2 --- lib/shield.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/shield.rb b/lib/shield.rb index c8018c1..a30633a 100644 --- a/lib/shield.rb +++ b/lib/shield.rb @@ -111,7 +111,7 @@ module Password # @see: https://www.djangoproject.com/weblog/2013/sep/15/security/ MAX_LEN = 4096 - def self.encrypt(password, salt = generate_salt, mode: :armor) + def self.encrypt(password, salt = generate_salt, mode: :argon2) @encryption_mode = mode case @encryption_mode From 5ac28a957a12bffeb0ccf9039b7aae45770ef9ae Mon Sep 17 00:00:00 2001 From: Herbert Joseph Date: Mon, 25 Jan 2016 23:35:51 -0500 Subject: [PATCH 7/7] Update shield.rb --- lib/shield.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/shield.rb b/lib/shield.rb index a30633a..ad9c1e0 100644 --- a/lib/shield.rb +++ b/lib/shield.rb @@ -93,8 +93,8 @@ def is_valid_password?(user, password) end end - def password=(password, mode: :armor) - self.crypted_password = Shield::Password.encrypt(password.to_s, mode: mode) + def password=(password) + self.crypted_password = Shield::Password.encrypt(password.to_s) end end