Permalink
Browse files

Provide direct access to scrypt function via scrypt_raw.

Implement specs for published SCrypt test vectors.
Tweak new/old-style hash detection to use a better regexp.
New-style hashes no longer include the cost parameter string with the the 16-byte hex salt.
  • Loading branch information...
nomoon committed May 4, 2012
1 parent 93c424d commit c5fd49e3fdf75453237cdad3d0ab9edfc77d4e93
Showing with 21 additions and 6 deletions.
  1. +11 −4 lib/scrypt.rb
  2. +10 −2 spec/scrypt/password_spec.rb
View
@@ -26,14 +26,21 @@ class Engine
private_class_method :__sc_calibrate
private_class_method :__sc_crypt
+ def self.scrypt_raw(secret, salt, n, r, p, key_len)
+ cost = "%x$%x$%x$" % [n, r, p]
+ __sc_crypt(secret, salt, cost, key_len)
+ end
+
# Given a secret and a valid salt (see SCrypt::Engine.generate_salt) calculates an scrypt password hash.
- def self.hash_secret(secret, salt, key_len = 32)
+ def self.hash_secret(secret, salt, key_len = DEFAULTS[:key_len])
if valid_secret?(secret)
if valid_salt?(salt)
cost = autodetect_cost(salt)
- if salt[-17,1] == "$" #Shorter salt means newer-style hash.
- salt + "$" + __sc_crypt(secret.to_s, salt, cost, key_len).unpack('H*').first.rjust(key_len * 2, '0')
- else #Longer salt means legacy-style hash.
+ #16-byte salt means newer-style hash. Longer means legacy-style hash. Shorter won't pass regexp.
+ if (salt_only = salt[/\$([A-Za-z0-9]{16})$/, 1])
+ #Don't send cost parameter as part of salt (it doesn't add any entropy).
+ salt + "$" + __sc_crypt(secret.to_s, salt_only, cost, key_len).unpack('H*').first.rjust(key_len * 2, '0')
+ else
salt + "$" + Digest::SHA1.hexdigest(__sc_crypt(secret.to_s, salt, cost, 256))
end
else
@@ -62,7 +62,16 @@
end
-describe "Legacy-style hashes" do
+describe "SCrypt function" do
+ it "should pass test vectors" do
+ SCrypt::Engine.scrypt_raw('', '', 16, 1, 1, 64).unpack('H*').first.should eq('77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906')
+ SCrypt::Engine.scrypt_raw('password', 'NaCl', 1024, 8, 16, 64).unpack('H*').first.should eq('fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640')
+ SCrypt::Engine.scrypt_raw('pleaseletmein', 'SodiumChloride', 16384, 8, 1, 64).unpack('H*').first.should eq('7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887')
+ SCrypt::Engine.scrypt_raw('pleaseletmein', 'SodiumChloride', 1048576, 8, 1, 64).unpack('H*').first.should eq('2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5ee9820adaa478e56fd8f4ba5d09ffa1c6d927c40f4c337304049e8a952fbcbf45c6fa77a41a4')
+ end
+end
+
+describe "Old-style hashes" do
before :each do
@secret = "my secret"
@hash = "400$8$d$173a8189751c095a29b933789560b73bf17b2e01$9bf66d74bd6f3ebcf99da3b379b689b89db1cb07"
@@ -72,4 +81,3 @@
(SCrypt::Password.new(@hash) == @secret).should be(true)
end
end
-

0 comments on commit c5fd49e

Please sign in to comment.