module Crypto.Saltine.Core.Password
( Salt
, newSalt
, needsRehash
, pwhashStr
, pwhashStrVerify
, pwhash
, Policy(..)
, interactivePolicy
, moderatePolicy
, sensitivePolicy
, Opslimit
, opslimit
, getOpslimit
, minOpslimit
, maxOpslimit
, opslimitInteractive
, opslimitModerate
, opslimitSensitive
, Memlimit
, memlimit
, getMemlimit
, minMemlimit
, maxMemlimit
, memlimitInteractive
, memlimitModerate
, memlimitSensitive
, Algorithm
, defaultAlgorithm
) where
import Crypto.Saltine.Internal.Util
import Crypto.Saltine.Internal.Password as I
import Data.ByteString (ByteString)
import Data.Text (Text)
import Foreign.C
import System.IO.Unsafe
import qualified Crypto.Saltine.Internal.Password as Bytes
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
newSalt :: IO Salt
newSalt :: IO Salt
newSalt = ByteString -> Salt
Salt (ByteString -> Salt) -> IO ByteString -> IO Salt
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> IO ByteString
randomByteString Int
Bytes.pwhash_saltbytes
pwhashStr :: Text -> Policy -> IO (Maybe PasswordHash)
pwhashStr :: Text -> Policy -> IO (Maybe PasswordHash)
pwhashStr Text
pw Policy
policy = do
let (CULLong
ops, CSize
mem, CInt
_alg) = Policy -> (CULLong, CSize, CInt)
unpackPolicy Policy
policy
(Maybe String -> Maybe PasswordHash)
-> IO (Maybe String) -> IO (Maybe PasswordHash)
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String -> PasswordHash) -> Maybe String -> Maybe PasswordHash
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Text -> PasswordHash
PasswordHash (Text -> PasswordHash)
-> (String -> Text) -> String -> PasswordHash
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack)) (IO (Maybe String) -> IO (Maybe PasswordHash))
-> IO (Maybe String) -> IO (Maybe PasswordHash)
forall a b. (a -> b) -> a -> b
$ Int -> (Ptr CChar -> IO (Maybe String)) -> IO (Maybe String)
forall a b. Int -> (Ptr a -> IO b) -> IO b
allocaBytes Int
pwhash_strbytes ((Ptr CChar -> IO (Maybe String)) -> IO (Maybe String))
-> (Ptr CChar -> IO (Maybe String)) -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$ \Ptr CChar
pp ->
[ByteString]
-> ([CStringLen] -> IO (Maybe String)) -> IO (Maybe String)
forall b. [ByteString] -> ([CStringLen] -> IO b) -> IO b
constByteStrings [Text -> ByteString
TE.encodeUtf8 Text
pw] (([CStringLen] -> IO (Maybe String)) -> IO (Maybe String))
-> ([CStringLen] -> IO (Maybe String)) -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$ \ [(Ptr CChar
ppw, Int
ppwlen)] -> do
CInt
ret <- Ptr CChar -> Ptr CChar -> CULLong -> CULLong -> CSize -> IO CInt
c_pwhash_str Ptr CChar
pp Ptr CChar
ppw (Int -> CULLong
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
ppwlen) (CULLong -> CULLong
forall a b. (Integral a, Num b) => a -> b
fromIntegral CULLong
ops) (CSize -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral CSize
mem)
case CInt
ret of
CInt
0 -> String -> Maybe String
forall a. a -> Maybe a
Just (String -> Maybe String) -> IO String -> IO (Maybe String)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr CChar -> IO String
peekCAString Ptr CChar
pp
CInt
_ -> Maybe String -> IO (Maybe String)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe String
forall a. Maybe a
Nothing
pwhashStrVerify :: PasswordHash -> Text -> Bool
pwhashStrVerify :: PasswordHash -> Text -> Bool
pwhashStrVerify (PasswordHash Text
h) Text
pw = IO Bool -> Bool
forall a. IO a -> a
unsafePerformIO (IO Bool -> Bool) -> IO Bool -> Bool
forall a b. (a -> b) -> a -> b
$
[ByteString] -> ([CStringLen] -> IO Bool) -> IO Bool
forall b. [ByteString] -> ([CStringLen] -> IO b) -> IO b
constByteStrings [Text -> ByteString
TE.encodeUtf8 (Text -> ByteString) -> Text -> ByteString
forall a b. (a -> b) -> a -> b
$ Text -> Char -> Text
T.snoc Text
h Char
'\NUL', Text -> ByteString
TE.encodeUtf8 Text
pw] (([CStringLen] -> IO Bool) -> IO Bool)
-> ([CStringLen] -> IO Bool) -> IO Bool
forall a b. (a -> b) -> a -> b
$ \[(Ptr CChar
ph, Int
_), (Ptr CChar
ppw, Int
ppwlen)] -> do
CInt
res <- Ptr CChar -> Ptr CChar -> CULLong -> IO CInt
c_pwhash_str_verify Ptr CChar
ph Ptr CChar
ppw (Int -> CULLong
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
ppwlen)
Bool -> IO Bool
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (CInt
res CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
0)
needsRehash :: Opslimit -> Memlimit -> PasswordHash -> Maybe Bool
needsRehash :: Opslimit -> Memlimit -> PasswordHash -> Maybe Bool
needsRehash (Opslimit Int
ops) (Memlimit Int
mem) (PasswordHash Text
h) =
IO (Maybe Bool) -> Maybe Bool
forall a. IO a -> a
unsafePerformIO (IO (Maybe Bool) -> Maybe Bool) -> IO (Maybe Bool) -> Maybe Bool
forall a b. (a -> b) -> a -> b
$
[ByteString]
-> ([CStringLen] -> IO (Maybe Bool)) -> IO (Maybe Bool)
forall b. [ByteString] -> ([CStringLen] -> IO b) -> IO b
constByteStrings [Text -> ByteString
TE.encodeUtf8 (Text -> ByteString) -> Text -> ByteString
forall a b. (a -> b) -> a -> b
$ Text -> Char -> Text
T.snoc Text
h Char
'\NUL'] (([CStringLen] -> IO (Maybe Bool)) -> IO (Maybe Bool))
-> ([CStringLen] -> IO (Maybe Bool)) -> IO (Maybe Bool)
forall a b. (a -> b) -> a -> b
$ \[(Ptr CChar
ph,Int
_)] -> do
CInt
res <- Ptr CChar -> CULLong -> CSize -> IO CInt
c_pwhash_str_needs_rehash Ptr CChar
ph (Int -> CULLong
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
ops) (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
mem)
Maybe Bool -> IO (Maybe Bool)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe Bool -> IO (Maybe Bool)) -> Maybe Bool -> IO (Maybe Bool)
forall a b. (a -> b) -> a -> b
$ if CInt
res CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== -CInt
1
then Maybe Bool
forall a. Maybe a
Nothing
else Bool -> Maybe Bool
forall a. a -> Maybe a
Just (CInt
res CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
1)
pwhash :: Text -> Int -> Salt -> Policy -> Maybe ByteString
pwhash :: Text -> Int -> Salt -> Policy -> Maybe ByteString
pwhash Text
pw Int
len (Salt ByteString
salt) Policy
policy = do
let (CULLong
ops, CSize
mem, CInt
alg) = Policy -> (CULLong, CSize, CInt)
unpackPolicy Policy
policy
let (CInt
e, ByteString
hashed) =
Int -> (Ptr CChar -> IO CInt) -> (CInt, ByteString)
forall b. Int -> (Ptr CChar -> IO b) -> (b, ByteString)
buildUnsafeByteString Int
len ((Ptr CChar -> IO CInt) -> (CInt, ByteString))
-> (Ptr CChar -> IO CInt) -> (CInt, ByteString)
forall a b. (a -> b) -> a -> b
$ \Ptr CChar
hbuf ->
[ByteString] -> ([CStringLen] -> IO CInt) -> IO CInt
forall b. [ByteString] -> ([CStringLen] -> IO b) -> IO b
constByteStrings [Text -> ByteString
TE.encodeUtf8 Text
pw, ByteString
salt] (([CStringLen] -> IO CInt) -> IO CInt)
-> ([CStringLen] -> IO CInt) -> IO CInt
forall a b. (a -> b) -> a -> b
$ \[(Ptr CChar
ppw,Int
ppwlen), (Ptr CChar
psalt,Int
_)] ->
Ptr CChar
-> CULLong
-> Ptr CChar
-> CULLong
-> Ptr CChar
-> CULLong
-> CSize
-> CInt
-> IO CInt
c_pwhash
Ptr CChar
hbuf (Int -> CULLong
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len)
Ptr CChar
ppw (Int -> CULLong
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
ppwlen)
Ptr CChar
psalt
(CULLong -> CULLong
forall a b. (Integral a, Num b) => a -> b
fromIntegral CULLong
ops)
(CSize -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral CSize
mem)
(Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> CInt) -> Int -> CInt
forall a b. (a -> b) -> a -> b
$ CInt -> Int
forall a. Enum a => a -> Int
fromEnum CInt
alg)
if CInt
e CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== -CInt
1
then Maybe ByteString
forall a. Maybe a
Nothing
else ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just ByteString
hashed
opslimit :: Algorithm -> Int -> Maybe Opslimit
opslimit :: Algorithm -> Int -> Maybe Opslimit
opslimit Algorithm
alg Int
x
| Int -> Opslimit
Opslimit Int
x Opslimit -> Opslimit -> Bool
forall a. Ord a => a -> a -> Bool
< Algorithm -> Opslimit
minOpslimit Algorithm
alg = Maybe Opslimit
forall a. Maybe a
Nothing
| Int -> Opslimit
Opslimit Int
x Opslimit -> Opslimit -> Bool
forall a. Ord a => a -> a -> Bool
> Algorithm -> Opslimit
maxOpslimit Algorithm
alg = Maybe Opslimit
forall a. Maybe a
Nothing
| Bool
otherwise = Opslimit -> Maybe Opslimit
forall a. a -> Maybe a
Just (Int -> Opslimit
Opslimit Int
x)
opslimitInteractive :: Algorithm -> Opslimit
opslimitInteractive :: Algorithm -> Opslimit
opslimitInteractive Algorithm
DefaultAlgorithm = Int -> Opslimit
Opslimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_opslimit_interactive)
opslimitInteractive Algorithm
Argon2i13 = Int -> Opslimit
Opslimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2i_opslimit_interactive)
opslimitInteractive Algorithm
Argon2id13 = Int -> Opslimit
Opslimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2id_opslimit_interactive)
opslimitModerate :: Algorithm -> Opslimit
opslimitModerate :: Algorithm -> Opslimit
opslimitModerate Algorithm
DefaultAlgorithm = Int -> Opslimit
Opslimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_opslimit_moderate)
opslimitModerate Algorithm
Argon2i13 = Int -> Opslimit
Opslimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2i_opslimit_moderate)
opslimitModerate Algorithm
Argon2id13 = Int -> Opslimit
Opslimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2id_opslimit_moderate)
opslimitSensitive :: Algorithm -> Opslimit
opslimitSensitive :: Algorithm -> Opslimit
opslimitSensitive Algorithm
DefaultAlgorithm = Int -> Opslimit
Opslimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_opslimit_sensitive)
opslimitSensitive Algorithm
Argon2i13 = Int -> Opslimit
Opslimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2i_opslimit_sensitive)
opslimitSensitive Algorithm
Argon2id13 = Int -> Opslimit
Opslimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2id_opslimit_sensitive)
memlimit :: Algorithm -> Int -> Maybe Memlimit
memlimit :: Algorithm -> Int -> Maybe Memlimit
memlimit Algorithm
alg Int
x
| Int -> Memlimit
Memlimit Int
x Memlimit -> Memlimit -> Bool
forall a. Ord a => a -> a -> Bool
< Algorithm -> Memlimit
minMemlimit Algorithm
alg = Maybe Memlimit
forall a. Maybe a
Nothing
| Int -> Memlimit
Memlimit Int
x Memlimit -> Memlimit -> Bool
forall a. Ord a => a -> a -> Bool
> Algorithm -> Memlimit
maxMemlimit Algorithm
alg= Maybe Memlimit
forall a. Maybe a
Nothing
| Bool
otherwise = Memlimit -> Maybe Memlimit
forall a. a -> Maybe a
Just (Int -> Memlimit
Memlimit Int
x)
memlimitInteractive :: Algorithm -> Memlimit
memlimitInteractive :: Algorithm -> Memlimit
memlimitInteractive Algorithm
DefaultAlgorithm = Int -> Memlimit
Memlimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_memlimit_interactive)
memlimitInteractive Algorithm
Argon2i13 = Int -> Memlimit
Memlimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2i_memlimit_interactive)
memlimitInteractive Algorithm
Argon2id13 = Int -> Memlimit
Memlimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2id_memlimit_interactive)
memlimitModerate :: Algorithm -> Memlimit
memlimitModerate :: Algorithm -> Memlimit
memlimitModerate Algorithm
DefaultAlgorithm = Int -> Memlimit
Memlimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_memlimit_moderate)
memlimitModerate Algorithm
Argon2i13 = Int -> Memlimit
Memlimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2i_memlimit_moderate)
memlimitModerate Algorithm
Argon2id13 = Int -> Memlimit
Memlimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2id_memlimit_moderate)
memlimitSensitive :: Algorithm -> Memlimit
memlimitSensitive :: Algorithm -> Memlimit
memlimitSensitive Algorithm
DefaultAlgorithm = Int -> Memlimit
Memlimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_memlimit_sensitive)
memlimitSensitive Algorithm
Argon2i13 = Int -> Memlimit
Memlimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2i_memlimit_sensitive)
memlimitSensitive Algorithm
Argon2id13 = Int -> Memlimit
Memlimit (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2id_memlimit_sensitive)
defaultAlgorithm :: Algorithm
defaultAlgorithm :: Algorithm
defaultAlgorithm = Algorithm
DefaultAlgorithm
unpackPolicy :: Policy -> (CULLong, CSize, CInt)
unpackPolicy :: Policy -> (CULLong, CSize, CInt)
unpackPolicy (Policy Opslimit
ops Memlimit
mem Algorithm
alg) =
( Int -> CULLong
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Opslimit -> Int
getOpslimit Opslimit
ops)
, Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Memlimit -> Int
getMemlimit Memlimit
mem)
, Algorithm -> CInt
algorithm Algorithm
alg
)
interactivePolicy :: Policy
interactivePolicy :: Policy
interactivePolicy = Opslimit -> Memlimit -> Algorithm -> Policy
Policy (Algorithm -> Opslimit
opslimitInteractive Algorithm
defaultAlgorithm)
(Algorithm -> Memlimit
memlimitInteractive Algorithm
defaultAlgorithm)
Algorithm
defaultAlgorithm
moderatePolicy :: Policy
moderatePolicy :: Policy
moderatePolicy = Opslimit -> Memlimit -> Algorithm -> Policy
Policy (Algorithm -> Opslimit
opslimitModerate Algorithm
defaultAlgorithm)
(Algorithm -> Memlimit
memlimitModerate Algorithm
defaultAlgorithm)
Algorithm
defaultAlgorithm
sensitivePolicy :: Policy
sensitivePolicy :: Policy
sensitivePolicy = Opslimit -> Memlimit -> Algorithm -> Policy
Policy (Algorithm -> Opslimit
opslimitSensitive Algorithm
defaultAlgorithm)
(Algorithm -> Memlimit
memlimitSensitive Algorithm
defaultAlgorithm)
Algorithm
defaultAlgorithm
minOpslimit :: Algorithm -> Opslimit
minOpslimit :: Algorithm -> Opslimit
minOpslimit Algorithm
DefaultAlgorithm = Int -> Opslimit
Opslimit (Int -> Opslimit) -> Int -> Opslimit
forall a b. (a -> b) -> a -> b
$ Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_opslimit_min
minOpslimit Algorithm
Argon2i13 = Int -> Opslimit
Opslimit (Int -> Opslimit) -> Int -> Opslimit
forall a b. (a -> b) -> a -> b
$ Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2i_opslimit_min
minOpslimit Algorithm
Argon2id13 = Int -> Opslimit
Opslimit (Int -> Opslimit) -> Int -> Opslimit
forall a b. (a -> b) -> a -> b
$ Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2id_opslimit_min
maxOpslimit :: Algorithm -> Opslimit
maxOpslimit :: Algorithm -> Opslimit
maxOpslimit Algorithm
DefaultAlgorithm = Int -> Opslimit
Opslimit (Int -> Opslimit) -> Int -> Opslimit
forall a b. (a -> b) -> a -> b
$ Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_opslimit_max
maxOpslimit Algorithm
Argon2i13 = Int -> Opslimit
Opslimit (Int -> Opslimit) -> Int -> Opslimit
forall a b. (a -> b) -> a -> b
$ Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2i_opslimit_max
maxOpslimit Algorithm
Argon2id13 = Int -> Opslimit
Opslimit (Int -> Opslimit) -> Int -> Opslimit
forall a b. (a -> b) -> a -> b
$ Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2id_opslimit_max
minMemlimit :: Algorithm -> Memlimit
minMemlimit :: Algorithm -> Memlimit
minMemlimit Algorithm
DefaultAlgorithm = Int -> Memlimit
Memlimit (Int -> Memlimit) -> Int -> Memlimit
forall a b. (a -> b) -> a -> b
$ Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_memlimit_min
minMemlimit Algorithm
Argon2i13 = Int -> Memlimit
Memlimit (Int -> Memlimit) -> Int -> Memlimit
forall a b. (a -> b) -> a -> b
$ Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2i_memlimit_min
minMemlimit Algorithm
Argon2id13 = Int -> Memlimit
Memlimit (Int -> Memlimit) -> Int -> Memlimit
forall a b. (a -> b) -> a -> b
$ Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2id_memlimit_min
maxMemlimit :: Algorithm -> Memlimit
maxMemlimit :: Algorithm -> Memlimit
maxMemlimit Algorithm
DefaultAlgorithm = Int -> Memlimit
Memlimit (Int -> Memlimit) -> Int -> Memlimit
forall a b. (a -> b) -> a -> b
$ Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_memlimit_max
maxMemlimit Algorithm
Argon2i13 = Int -> Memlimit
Memlimit (Int -> Memlimit) -> Int -> Memlimit
forall a b. (a -> b) -> a -> b
$ Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2i_memlimit_max
maxMemlimit Algorithm
Argon2id13 = Int -> Memlimit
Memlimit (Int -> Memlimit) -> Int -> Memlimit
forall a b. (a -> b) -> a -> b
$ Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
Bytes.pwhash_argon2id_memlimit_max