Class: SCrypt::Engine
- Inherits:
-
Object
- Object
- SCrypt::Engine
- Defined in:
- lib/scrypt-ruby.rb
Overview
Password
Constant Summary collapse
- DEFAULTS =
{ key_len: 32, salt_size: 32, max_mem: 16 * 1024 * 1024, # 16 MiB max_memfrac: 0.5, max_time: 0.2, cost: nil }
Class Method Summary collapse
-
.calibrate(options = {}) ⇒ Object
N r p “400$8$25$”.
-
.calibrate!(options = {}) ⇒ Object
N r p “4000$8$4$”.
- .debug(*args) ⇒ Object
-
.generate_salt(options = {}) ⇒ Object
“4000$8$4$c6d101522d3cb045”.
-
.hash_secret(secret, salt, key_len = ) ⇒ Object
“400$8$26$b62e0f787a5fc373$0399ccd4fa26642d92741b17c366b7f6bd12ccea5214987af445d2bed97bc6a2”.
- .hash_secret_new(secret, salt_only, cost, key_len) ⇒ Object
- .hash_secret_old(secret, salt, cost) ⇒ Object
- .scrypt(plaintext_password, salt, *args) ⇒ Object
- .valid_cost?(cost) ⇒ Boolean
- .valid_salt?(salt) ⇒ Boolean
- .valid_secret?(secret) ⇒ Boolean
Class Method Details
.calibrate(options = {}) ⇒ Object
N r p “400$8$25$”
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/scrypt-ruby.rb', line 115 def self.calibrate( = {}) = DEFAULTS.merge() max_mem = .delete :max_mem max_memfrac = .delete :max_memfrac max_time = .delete :max_time mem_limit = memtouse(max_mem, max_memfrac) ops_limit = [cpuperf * max_time, 32768].min debug "ops_limit #{ops_limit} ops" n, r, p = nil, 8, nil if ops_limit < mem_limit/32 debug "pick based on CPU limit" # Set p = 1 and choose N based on the CPU limit. p = 1 max_n = ops_limit / (r * 4) n = 1 << (1...63).find { |i| (1 << i) > max_n / 2 } else debug "pick based on memory limit" # Set N based on the memory limit. */ max_n = mem_limit / (r * 128) n = 1 << (1..63).find { |i| (1 << i) > max_n / 2 } # Choose p based on the CPU limit. */ max_rp = [(ops_limit / 4) / (1 << n), 0x3fffffff].min p = max_rp / r end debug "calibrated using: N #{n} r #{r} p #{p}" "#{n.to_s(16)}$#{r.to_s(16)}$#{p.to_s(16)}$" end |
.calibrate!(options = {}) ⇒ Object
N r p “4000$8$4$”
105 106 107 |
# File 'lib/scrypt-ruby.rb', line 105 def self.calibrate!( = {}) DEFAULTS[:cost] = calibrate() end |
.debug(*args) ⇒ Object
109 110 111 |
# File 'lib/scrypt-ruby.rb', line 109 def self.debug(*args) SCrypt.debug(*args) end |
.generate_salt(options = {}) ⇒ Object
“4000$8$4$c6d101522d3cb045”
91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/scrypt-ruby.rb', line 91 def self.generate_salt( = {}) = DEFAULTS.merge() cost = [:cost] || calibrate() salt_size = .delete :salt_size salt = OpenSSL::Random.random_bytes(salt_size).unpack('H*').first.rjust(16,'0') if salt.length == 40 #If salt is 40 characters, the regexp will think that it is an old-style hash, so add a '0'. salt = '0' + salt end cost + salt end |
.hash_secret(secret, salt, key_len = ) ⇒ Object
“400$8$26$b62e0f787a5fc373$0399ccd4fa26642d92741b17c366b7f6bd12ccea5214987af445d2bed97bc6a2”
172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/scrypt-ruby.rb', line 172 def self.hash_secret(secret, salt, key_len = DEFAULTS[:key_len]) raise Errors::InvalidSalt, "invalid salt" unless valid_salt? salt raise Errors::InvalidSecret, "invalid secret" unless valid_secret? secret cost = cost_from_salt(salt) salt_only = salt_only(salt) if salt_only.length == 40 hash_secret_old(secret, salt, cost) else salt + "$" + hash_secret_new(secret, salt_only, cost, key_len) end end |
.hash_secret_new(secret, salt_only, cost, key_len) ⇒ Object
166 167 168 169 |
# File 'lib/scrypt-ruby.rb', line 166 def self.hash_secret_new(secret, salt_only, cost, key_len) salt_only = [salt_only.sub(/^(00)+/, '')].pack('H*') scrypt(secret.to_s, salt_only, cost, key_len).unpack('H*').first.rjust(key_len * 2, '0') end |
.hash_secret_old(secret, salt, cost) ⇒ Object
162 163 164 |
# File 'lib/scrypt-ruby.rb', line 162 def self.hash_secret_old(secret, salt, cost) salt + "$" + Digest::SHA1.hexdigest(scrypt(secret.to_s, salt, cost, 256)) end |
.scrypt(plaintext_password, salt, *args) ⇒ Object
75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/scrypt-ruby.rb', line 75 def self.scrypt(plaintext_password, salt, *args) key_len = args.last n = r = p = nil case args.length when 2 n, r, p = args[0].split('$').map{ |x| x.to_i(16) } when 4 n, r, p = args else raise ArgumentError, 'only 4 or 6 arguments allowed' end raw_encrypted_password = com.lambdaworks.crypto.SCrypt.scrypt(plaintext_password.to_s.to_java_bytes, salt.to_java_bytes, n, r, p, key_len) String.from_java_bytes raw_encrypted_password end |
.valid_cost?(cost) ⇒ Boolean
148 149 150 |
# File 'lib/scrypt-ruby.rb', line 148 def self.valid_cost?(cost) !!cost.match(/^[0-9a-z]+\$[0-9a-z]+\$[0-9a-z]+\$$/) end |
.valid_salt?(salt) ⇒ Boolean
153 154 155 |
# File 'lib/scrypt-ruby.rb', line 153 def self.valid_salt?(salt) !!salt.match(/^[0-9a-z]+\$[0-9a-z]+\$[0-9a-z]+\$[A-Za-z0-9]{16,64}$/) end |
.valid_secret?(secret) ⇒ Boolean
158 159 160 |
# File 'lib/scrypt-ruby.rb', line 158 def self.valid_secret?(secret) secret.respond_to?(:to_s) end |