module Data.Digest.BCrypt
( bcrypt
, genSalt
, packBSalt
, BSalt
)
where
import Foreign
import Foreign.C.Types
import Foreign.C.String
import qualified Foreign.Ptr ( nullPtr )
import qualified System.IO.Unsafe ( unsafePerformIO )
import Data.ByteString.Char8 (split)
import qualified Data.ByteString.Unsafe as B
import qualified Data.ByteString.Internal as B ( fromForeignPtr
, c_strlen
)
import qualified Data.ByteString as B
newtype BSalt = BSalt {
unBSalt::B.ByteString
} deriving (Eq, Ord, Show)
packBSalt :: B.ByteString
-> Maybe BSalt
packBSalt s = do
let unpacked = split '$' s in
case unpacked of
_:version:rounds:hashed:xs ->
let vlen = B.length version
rlen = B.length rounds
hlen = B.length hashed in
if and $ (vlen >= 2) : (rlen >= 1) : [hlen == 53]
then Just $ BSalt s
else Nothing
_ -> Nothing
genSalt :: Integer
-> B.ByteString
-> Maybe BSalt
genSalt cost seed
| B.length seed /= 16 = Nothing
| otherwise = unsafePerformIO $
B.useAsCString seed $ \s ->
allocaBytes 30 $ \out -> do
let seed' = (fromIntegral cost::CInt)
bsalt <- c_bcrypt_gensalt out seed' (castPtr s)
result <- B.packCString bsalt
return $ Just $ BSalt result
bcrypt :: B.ByteString
-> BSalt
-> B.ByteString
bcrypt key (BSalt salt) = unsafePerformIO $
B.useAsCString key $ \k -> B.useAsCString salt $ \s ->
allocaBytes 128 $ \out -> do
cptr <- c_bcrypt out k s
if cptr == nullPtr
then return B.empty
else B.packCString cptr
foreign import ccall unsafe "bcrypt.h bcrypt_gensalt"
c_bcrypt_gensalt :: CString -> CInt -> Ptr Word8 -> IO CString
foreign import ccall unsafe "bcrypt.h bcrypt"
c_bcrypt :: CString -> CString -> CString -> IO CString