packages feed

botan-low-0.0.2.0: test/Botan/Low/PwdHashSpec.hs

module Main where

import Data.ByteString (isPrefixOf)

import Botan.Low.Hash
import Botan.Low.MAC
import Botan.Low.PwdHash

import Test.Prelude

-- NOTE: Needs more exhaustive tests, and to validate parameter order for
--  Scrypt and the Argons. We've got tests that pass, but that doesn't
--  mean that they're being used properly. Need to investigate C++ source.

-- NOTE: Values generated by "pwdhashTimed pbkdf 200 64 passphrase salt"
pbkdfs :: [(PBKDFName, Int, Int, Int)]
pbkdfs =
    [ ("PBKDF2(HMAC(SHA-512))",138000,0,0)
    -- NOTE: These results indicate that the parameter result order is inconsistent
    --  for pwdhashTimed compared to pwdhash
    --  Eg, Scrypt should be n, r, p but n=8192 is clearly last
    --  Same for the argons
    -- This causes both tests to fail (original generated values)
    -- , ("Scrypt",1,81,8192)
    -- , ("Argon2d",1,1,262144)
    -- , ("Argon2i",1,1,262144)
    -- , ("Argon2id",1,1,262144)
    -- Note that this still causes four pwdhashTimed tests to still fail because
    --  the pwdhashTimed function itself needs to be fixed for these algorithms
    --  Fixed values, assuming only x/z are flipped a la (x,y,z) -> (z,y,x)
    --  But
    , ("Scrypt",8192,81,1)
    , ("Argon2d",262144,1,1)
    , ("Argon2i",262144,1,1)
    , ("Argon2id",262144,1,1)
    , ("Bcrypt-PBKDF",26,0,0)
    , ("OpenPGP-S2K(SHA-512)",65011712,0,0)
    ]

passphrase :: ByteString
passphrase = "Fee fi fo fum!"

salt :: ByteString
salt = "salt"

main :: IO ()
main = hspec $ testSuite pbkdfs (\(n,_,_,_) -> chars n) $ \ (pbkdf, i, j, k) -> do
    it "pwdhash" $ do
        _ <- pwdhash pbkdf i j k 64 passphrase salt
        pass
    it "pwdhashTimed" $ do
        timed@(i',j',k',pwd) <- pwdhashTimed pbkdf 200 64 passphrase salt
        -- NOTE: Fails parity for Scrypt and the Argons due to parameter order
        -- pwd' <- pwdhash pbkdf i' j' k' 64 passphrase salt
        --NOTE: Scrypt still fails parity and flipping j and k doesn't matter.
        pwd' <- case pbkdf of
            "Scrypt"                        -> pwdhash pbkdf j' k' i' 64 passphrase salt
            _ | "Argon" `isPrefixOf` pbkdf  -> pwdhash pbkdf k' j' i' 64 passphrase salt
            _                               -> pwdhash pbkdf i' j' k' 64 passphrase salt
        pwd `shouldBe` pwd'
        pass