Class: Reedb::SecureHash

Inherits:
Object
  • Object
show all
Defined in:
lib/reedb/security/secure_hash.rb

Constant Summary collapse

PBKDF2_ITERATIONS =

Constants for the hashing process. Can be changed without breaking existing hashes!

10000
SALT_BYTE_SIZE =
32
HASH_BYTE_SIZE =
24
HASH_SECTIONS =
3
SECTION_DELIMITER =
'::'
ITERATIONS_INDEX =
0
SALT_INDEX =
1
HASH_INDEX =
2

Class Method Summary collapse

Class Method Details

.assert(truth, msg) ⇒ Object



122
123
124
125
126
127
128
129
# File 'lib/reedb/security/secure_hash.rb', line 122

def self.assert( truth, msg )
  if truth
    puts "PASS [#{msg}]"
  else
    puts "FAIL [#{msg}]"
    @@allPass = false
  end
end

.runSelfTestsObject

Run tests to ensure the module is functioning properly. Returns true if all tests succeed, false if not.



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/reedb/security/secure_hash.rb', line 94

def self.runSelfTests
  puts "Sample hashes:"
  3.times { puts secure_hash("password") }

  puts "\nRunning self tests..."
  @@allPass = true

  correctPassword = 'aaaaaaaaaa'
  wrongPassword = 'aaaaaaaaab'
  hash = secure_hash(correctPassword)

  assert( validate_pw( correctPassword, hash ) == true, "correct password" )
  assert( validate_pw( wrongPassword, hash ) == false, "wrong password" )

  h1 = hash.split( SECTION_DELIMITER )
  h2 = secure_hash( correctPassword ).split( SECTION_DELIMITER )
  assert( h1[HASH_INDEX] != h2[HASH_INDEX], "different hashes" )
  assert( h1[SALT_INDEX] != h2[SALT_INDEX], "different salt" )

  if @@allPass
    puts "*** ALL TESTS PASS ***"
  else
    puts "*** FAILURES ***"
  end

  return @@allPass
end

.secure_hash(password) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/reedb/security/secure_hash.rb', line 53

def self.secure_hash password
  salt = SecureRandom.base64(SALT_BYTE_SIZE)
  sha256 = OpenSSL::Digest::SHA256.new
  len = sha256.digest_length
  pbkdf2 = OpenSSL::PKCS5.pbkdf2_hmac(
    password,
    salt,
    PBKDF2_ITERATIONS,
    len,
    sha256
  )

  return [PBKDF2_ITERATIONS, salt, Base64.encode64( pbkdf2 )].join( SECTION_DELIMITER )
end

.validate_pw(password, correctHash) ⇒ Object

Checks if a password is correct given a hash of the correct one. correctHash must be a hash string generated with createHash.



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/reedb/security/secure_hash.rb', line 70

def self.validate_pw(password, correctHash)
  params = correctHash.split(SECTION_DELIMITER)
  return false if params.length != HASH_SECTIONS

  salt = params[SALT_INDEX]
  hash = [HASH_INDEX]

  sha256 = OpenSSL::Digest::SHA256.new
  len = sha256.digest_length

  confirmed = Base64.decode64(params[HASH_INDEX])
  test_hash = OpenSSL::PKCS5.pbkdf2_hmac(
    password,
    salt,
    PBKDF2_ITERATIONS,
    len,
    sha256
  )

  return confirmed == test_hash
end