Add libsodium password hashing

master
Didactic Drunk 2019-05-28 16:15:13 -07:00
parent a358929e62
commit a26800765e
3 changed files with 101 additions and 1 deletions

22
spec/cox/pwhash_spec.cr Normal file
View File

@ -0,0 +1,22 @@
require "../spec_helper"
describe Cox::Pwhash do
it "hashes and verifies a password" do
pwhash = Cox::Pwhash.new
# set to minimum to speed up tests
pwhash.memlimit = Cox::Pwhash::MEMLIMIT_MIN
pwhash.opslimit = Cox::Pwhash::OPSLIMIT_MIN
pass = "1234"
hash = pwhash.hash_str pass
pwhash.verify hash, pass
expect_raises(Cox::Pwhash::PasswordVerifyError) do
pwhash.verify hash, "5678"
end
pwhash.needs_rehash?(hash).should be_false
pwhash.opslimit = Cox::Pwhash::OPSLIMIT_MAX
pwhash.needs_rehash?(hash).should be_true
end
end

View File

@ -12,6 +12,15 @@ module Cox
fun crypto_sign_bytes() : LibC::SizeT fun crypto_sign_bytes() : LibC::SizeT
fun crypto_kdf_keybytes() : LibC::SizeT fun crypto_kdf_keybytes() : LibC::SizeT
fun crypto_kdf_contextbytes() : LibC::SizeT fun crypto_kdf_contextbytes() : LibC::SizeT
fun crypto_pwhash_memlimit_min() : LibC::SizeT
fun crypto_pwhash_memlimit_interactive() : LibC::SizeT
fun crypto_pwhash_memlimit_max() : LibC::SizeT
fun crypto_pwhash_opslimit_min() : LibC::SizeT
fun crypto_pwhash_opslimit_interactive() : LibC::SizeT
fun crypto_pwhash_opslimit_moderate() : LibC::SizeT
fun crypto_pwhash_opslimit_sensitive() : LibC::SizeT
fun crypto_pwhash_opslimit_max() : LibC::SizeT
fun crypto_pwhash_strbytes() : LibC::SizeT
PUBLIC_KEY_BYTES = crypto_box_publickeybytes() PUBLIC_KEY_BYTES = crypto_box_publickeybytes()
SECRET_KEY_BYTES = crypto_box_secretkeybytes() SECRET_KEY_BYTES = crypto_box_secretkeybytes()
@ -22,6 +31,7 @@ module Cox
SIGNATURE_BYTES = crypto_sign_bytes() SIGNATURE_BYTES = crypto_sign_bytes()
KDF_KEY_BYTES = crypto_kdf_keybytes() KDF_KEY_BYTES = crypto_kdf_keybytes()
KDF_CONTEXT_BYTES = crypto_kdf_contextbytes() KDF_CONTEXT_BYTES = crypto_kdf_contextbytes()
PWHASH_STR_BYTES = crypto_pwhash_strbytes()
fun crypto_box_keypair( fun crypto_box_keypair(
public_key_output : Pointer(LibC::UChar), public_key_output : Pointer(LibC::UChar),
@ -66,7 +76,6 @@ module Cox
public_key : Pointer(LibC::UChar) public_key : Pointer(LibC::UChar)
) : LibC::Int ) : LibC::Int
fun crypto_kdf_derive_from_key( fun crypto_kdf_derive_from_key(
subkey : Pointer(LibC::UChar), subkey : Pointer(LibC::UChar),
subkey_len : LibC::SizeT, subkey_len : LibC::SizeT,
@ -74,5 +83,25 @@ module Cox
ctx : Pointer(LibC::UChar), ctx : Pointer(LibC::UChar),
key : Pointer(LibC::UChar) key : Pointer(LibC::UChar)
) : LibC::Int ) : LibC::Int
fun crypto_pwhash_str(
outstr : Pointer(LibC::UChar),
pass : Pointer(LibC::UChar),
pass_size : LibC::ULongLong,
optslimit : LibC::ULongLong,
memlimit : LibC::SizeT,
) : LibC::Int
fun crypto_pwhash_str_verify(
str : Pointer(LibC::UChar),
pass : Pointer(LibC::UChar),
pass_size : LibC::ULongLong,
) : LibC::Int
fun crypto_pwhash_str_needs_rehash(
str : Pointer(LibC::UChar),
optslimit : LibC::ULongLong,
memlimit : LibC::SizeT,
) : LibC::Int
end end
end end

49
src/cox/pwhash.cr Normal file
View File

@ -0,0 +1,49 @@
module Cox
class Pwhash
class PasswordVerifyError < Cox::Error
end
OPSLIMIT_MIN = LibSodium.crypto_pwhash_opslimit_min
OPSLIMIT_INTERACTIVE = LibSodium.crypto_pwhash_opslimit_interactive
OPSLIMIT_MODERATE = LibSodium.crypto_pwhash_opslimit_moderate
OPSLIMIT_SENSITIVE = LibSodium.crypto_pwhash_opslimit_sensitive
OPSLIMIT_MAX = LibSodium.crypto_pwhash_opslimit_max
MEMLIMIT_MIN = LibSodium.crypto_pwhash_memlimit_min
MEMLIMIT_MAX = LibSodium.crypto_pwhash_memlimit_max
MEMLIMIT_INTERACTIVE = LibSodium.crypto_pwhash_memlimit_interactive
property opslimit = OPSLIMIT_INTERACTIVE
property memlimit = MEMLIMIT_INTERACTIVE
def hash_str(pass)
outstr = Bytes.new LibSodium::PWHASH_STR_BYTES
if LibSodium.crypto_pwhash_str(outstr, pass, pass.bytesize, @opslimit, @memlimit) != 0
raise Cox::Error.new("crypto_pwhash_str")
end
outstr
end
def verify(str, pass)
# BUG: verify str length
case LibSodium.crypto_pwhash_str_verify(str, pass, pass.bytesize)
when 0
true
else
raise PasswordVerifyError.new
end
end
def needs_rehash?(str)
# BUG: verify str length
case LibSodium.crypto_pwhash_str_needs_rehash(str, @opslimit, @memlimit)
when 0
false
when 1
true
else
raise Cox::Error.new("crypto_pwhash_str_needs_rehash")
end
end
end
end