packages feed

scrypt 0.3.2 → 0.3.3

raw patch · 4 files changed

+381/−244 lines, 4 filesdep +HUnitdep +QuickCheckdep +scryptdep ~basedep ~base64-bytestringdep ~entropy

Dependencies added: HUnit, QuickCheck, scrypt, test-framework, test-framework-hunit, test-framework-quickcheck2

Dependency ranges changed: base, base64-bytestring, entropy

Files

− Crypto/Scrypt.hs
@@ -1,235 +0,0 @@-{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings,-    RecordWildCards, NamedFieldPuns #-}---- |Scrypt is a sequential memory-hard key derivation function. This module---  provides low-level bindings to the 'scrypt' key derivation function as---  well as a higher-level password-storage API. It is based on a fast C---  implementation of scrypt, written by Colin Percival. For further---  information see <http://www.tarsnap.com/scrypt.html>.----module Crypto.Scrypt (-    -- * Parameters to the @scrypt@ function-    -- $params-     ScryptParams, scryptParams, defaultParams-    -- * Password Storage-    -- $password-storage-    , EncryptedPass(..), encryptPass, encryptPass', verifyPass, verifyPass'-    -- * Low-level bindings to the @scrypt@ key derivation function-    -- $low-level-    , Pass(..), Salt(..), PassHash(..), scrypt, scrypt'-    ) where--import Control.Applicative-import qualified Data.ByteString.Base64 as Base64-import Data.ByteString.Char8 hiding (map, concat)-import Data.Maybe-import Foreign-import Foreign.C-import System.Entropy (getEntropy)---newtype Pass          = Pass     { unPass :: ByteString } deriving (Show, Eq)-newtype Salt          = Salt     { unSalt :: ByteString } deriving (Show, Eq)-newtype PassHash      = PassHash { unHash :: ByteString } deriving (Show,Eq)-newtype EncryptedPass =-    EncryptedPass { unEncryptedPass  :: ByteString } deriving (Show, Eq)----------------------------------------------------------------------------------- $params------ Scrypt takes three tuning parameters: @N@, @r@ and @p@. They affect running--- time and memory usage:------ /Memory usage/ is approximately @128*r*N@ bytes. Note that the--- 'scryptParams' function takes @log_2(N)@ as a parameter. As an example,--- the 'defaultParams'------ >   log_2(N) = 14, r = 8 and p = 1------ lead to 'scrypt' using @128 * 8 * 2^14 = 16M@ bytes of memory.------ /Running time/ is proportional to all of @N@, @r@ and @p@. Since it's--- influence on memory usage is small, @p@ can be used to independently tune--- the running time.------- |Encapsulates the three tuning parameters to the 'scrypt' function: @N@,--- @r@ and @p@ (see above).----data ScryptParams = Params { logN, r, p, bufLen :: Integer} deriving (Eq)--instance Show ScryptParams where-    show Params{..} = concat [ "ScryptParams "-        , "{ logN=", show logN-        , ", r="   , show r-        , ", p="   , show p-        , " }"-        ]---- |Constructor function for the 'ScryptParams' data type----scryptParams-    :: Integer-    -- ^ @log_2(N)@. Scrypt's @N@ parameter must be a power of two greater-    --   than one, thus it's logarithm to base two must be greater than zero.-    --   @128*r*N@ must be smaller than the available memory address space.-    -> Integer-    -- ^ The parameter @r@, must be greater than zero.-    -> Integer-    -- ^ The parameter @p@, must be greater than zero. @r@ and @p@-    --   must satisfy @r*p < 2^30@.-    -> Maybe ScryptParams-    -- ^ Returns 'Just' the parameter object for valid arguments,-    --   otherwise 'Nothing'.-    ---scryptParams logN r p | valid     = Just ps-                      | otherwise = Nothing-  where-    ps    = Params { logN, r, p, bufLen = 64 }-    valid = and [ logN > 0, r > 0, p > 0-                , r*p < 2^(30 :: Int)-                , bufLen ps <= 2^(32 :: Int)-1 * 32-                -- allocation fits into (virtual) memory-                , 128*r*2^logN <= fromIntegral (maxBound :: CSize)-                ]---- |Default parameters as recommended in the scrypt paper:------  >   N = 2^14, r = 8, p = 1------  Equivalent to @'fromJust' ('scryptParams' 14 8 1)@.----defaultParams :: ScryptParams-defaultParams = fromJust (scryptParams 14 8 1)----------------------------------------------------------------------------------- $password-storage------ To allow storing encrypted passwords conveniently in a single database--- column, the password storage API provides the data type 'EncryptedPass'. It--- combines a 'Pass' as well as the 'Salt' and 'ScryptParams' used to compute--- it into a single 'ByteString', separated by pipe (\"|\") characters. The--- 'Salt' and 'PassHash' are base64-encoded. Storing the 'ScryptParams' with --- the password allows to gradually strengthen password encryption in case of--- changing security requirements.------ A usage example is given below, showing encryption, verification and--- changing 'ScryptParams':------ > >>> encrypted <- encryptPass defaultParams (Pass "secret")--- > >>> print encrypted--- > EncryptedPass {unEncryptedPass = "14|8|1|Wn5x[SNIP]nM=|Zl+p[SNIP]g=="}--- > >>> print $ verifyPass defaultParams (Pass "secret") encrypted--- > (True,Nothing)--- > >>> print $ verifyPass defaultParams (Pass "wrong") encrypted--- > (False,Nothing)--- > >>> let newParams = fromJust $ scryptParams 16 8 1--- > >>> print $ verifyPass newParams (Pass "secret") encrypted--- > (True,Just (EncryptedPass {unEncryptedPass = "16|8|1|Wn5x[SNIP]nM=|ZmWw[SNIP]Q=="}))-----combine :: ScryptParams -> Salt -> PassHash -> EncryptedPass-combine Params{..} (Salt salt) (PassHash passHash) =-    EncryptedPass $ intercalate "|"-        [ showBS logN, showBS r, showBS p-        , Base64.encode salt, Base64.encode passHash]-  where-    showBS = pack . show--separate :: EncryptedPass -> Maybe (ScryptParams, Salt, PassHash)-separate = go . split '|' . unEncryptedPass-  where-    go [logN', r', p', salt', hash'] = do-        [salt, hash] <- mapM decodeBase64 [salt', hash']-        [logN, r, p] <- mapM (fmap fst . readInteger) [logN', r', p']-        params       <- scryptParams logN r p-        return (params, Salt salt, PassHash hash)-    go _         = Nothing-    decodeBase64 = either (const Nothing) Just . Base64.decode---- |Encrypt the password with the given parameters and a random 32-byte salt.--- The salt is read from @\/dev\/urandom@ on Unix systems or @CryptoAPI@ on--- Windows.----encryptPass :: ScryptParams -> Pass -> IO EncryptedPass-encryptPass params pass = do-    salt <- Salt <$> getEntropy 32-    return $ combine params salt (scrypt params salt pass)---- |Equivalent to @encryptPass defaultParams@.----encryptPass' :: Pass -> IO EncryptedPass-encryptPass' = encryptPass defaultParams---- |Verify a 'Pass' against an 'EncryptedPass'. The function also takes---  'ScryptParams' meeting your current security requirements. In case the---  'EncryptedPass' was generated with different parameters, the function---  returns an updated 'EncryptedPass', generated with the given ---  'ScryptParams'. The 'Salt' is kept from the given 'EncryptedPass'.----verifyPass-    :: ScryptParams-    -- ^ Parameters to use for updating the 'EncryptedPass'.-    -> Pass-    -- ^ The candidate 'Pass'.-    -> EncryptedPass-    -- ^ The 'EncryptedPass' to check against.-    -> (Bool, Maybe EncryptedPass)-    -- ^ Returns a pair of-    ---    --     * 'Bool' indicating verification success or failure.-    ---    --     * 'Just' a /new/ 'EncryptedPass' if the given 'ScryptParams' are-    --      different from those encapsulated in the /given/ 'EncryptedPass',-    --      otherwise 'Nothing'.-    ---verifyPass newParams candidate encrypted =-    maybe (False, Nothing) verify (separate encrypted)-  where-    verify (params,salt,hash) =-        let valid   = scrypt params salt candidate == hash-            newHash = scrypt newParams salt candidate-            newEncr = if not valid || params == newParams-                        then Nothing-                        else Just (combine newParams salt newHash)-        in (valid, newEncr)---- |Check the 'Pass' against the 'EncryptedPass', using the 'ScryptParams'---  encapsulated in the 'EncryptedPass'.----verifyPass' :: Pass -> EncryptedPass -> Bool-verifyPass' pass encrypted = fst $ verifyPass defaultParams pass encrypted----------------------------------------------------------------------------------- $low-level------ Bindings to a fast C implementation of 'scrypt'. For password storage,--- consider using the more convenient higher-level API above.------- |Calculates a 64-byte hash from the given password, salt and parameters.----scrypt :: ScryptParams -> Salt -> Pass -> PassHash-scrypt Params{..} (Salt salt) (Pass pass) =-    PassHash <$> unsafePerformIO $-        useAsCStringLen salt $ \(saltPtr, saltLen) ->-        useAsCStringLen pass $ \(passPtr, passLen) ->-        allocaBytes (fromIntegral bufLen) $ \bufPtr -> do-            throwErrnoIfMinus1_ "crypto_scrypt" $ crypto_scrypt-                (castPtr passPtr) (fromIntegral passLen)-                (castPtr saltPtr) (fromIntegral saltLen)-                (2^logN) (fromIntegral r) (fromIntegral p)-                bufPtr (fromIntegral bufLen)-            packCStringLen (castPtr bufPtr, fromIntegral bufLen)--foreign import ccall unsafe crypto_scrypt-    :: Ptr Word8 -> CSize         -- password-    -> Ptr Word8 -> CSize         -- salt-    -> Word64 -> Word32 -> Word32 -- N, r, p-    -> Ptr Word8 -> CSize         -- result buffer-    -> IO CInt---- |Note the prime symbol (\'). Calls 'scrypt' with 'defaultParams'.----scrypt' :: Salt -> Pass -> PassHash-scrypt' = scrypt defaultParams
scrypt.cabal view
@@ -1,5 +1,5 @@ name:           scrypt-version:        0.3.2+version:        0.3.3 license:        BSD3 license-file:   LICENSE category:       Cryptography@@ -7,7 +7,6 @@ author:         Falko Peters <falko.peters@gmail.com> maintainer:     Falko Peters <falko.peters@gmail.com> stability:      experimental-tested-with:    GHC == 6.12.3 cabal-version:  >= 1.8 homepage:       http://github.com/informatikr/scrypt bug-reports:    http://github.com/informatikr/scrypt/issues@@ -30,14 +29,18 @@     cbits/sha256.h,     cbits/sysendian.h +source-repository head+    type:     git+    location: http://github.com/informatikr/scrypt+ library     exposed-modules: Crypto.Scrypt-+    hs-source-dirs: src     build-depends:         base == 4.*,-        base64-bytestring == 0.1.*,+        base64-bytestring >= 0.1 && < 2,         bytestring == 0.9.*,-        entropy == 0.2+        entropy == 0.2.*      ghc-options: -Wall     ghc-prof-options: -auto-all@@ -47,7 +50,19 @@     include-dirs: cbits     includes: crypto_scrypt.h     install-includes: crypto_scrypt.h-    -source-repository head-    type:     git-    location: http://github.com/informatikr/scrypt++test-suite scrypt-test+    type: exitcode-stdio-1.0+    hs-source-dirs: test+    main-is: Test.hs+    build-depends:+        scrypt,+        base == 4.*,+        bytestring == 0.9.*,+        HUnit == 1.2.*,+        QuickCheck == 2.*,+        test-framework == 0.6.*,+        test-framework-hunit == 0.2.*,+        test-framework-quickcheck2 == 0.2.*+    ghc-options: -Wall+    ghc-prof-options: -auto-all
+ src/Crypto/Scrypt.hs view
@@ -0,0 +1,238 @@+{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings,+    RecordWildCards, NamedFieldPuns #-}++-- |Scrypt is a sequential memory-hard key derivation function. This module+--  provides low-level bindings to the 'scrypt' key derivation function as+--  well as a higher-level password-storage API. It is based on a fast C+--  implementation of scrypt, written by Colin Percival. For further+--  information see <http://www.tarsnap.com/scrypt.html>.+--+module Crypto.Scrypt (+    -- * Parameters to the @scrypt@ function+    -- $params+     ScryptParams, scryptParams, defaultParams+    -- * Password Storage+    -- $password-storage+    , EncryptedPass(..), encryptPass, encryptPass', verifyPass, verifyPass'+    -- * Low-level bindings to the @scrypt@ key derivation function+    -- $low-level+    , Pass(..), Salt(..), PassHash(..), scrypt, scrypt'+    ) where++import Control.Applicative+import qualified Data.ByteString.Base64 as Base64+import Data.ByteString.Char8 hiding (map, concat)+import Data.Maybe+import Foreign (Ptr, Word8, Word32, Word64, allocaBytes, castPtr)+import Foreign.C+import System.Entropy (getEntropy)+import System.IO.Unsafe (unsafePerformIO)+++newtype Pass          = Pass     { unPass :: ByteString } deriving (Show, Eq)+newtype Salt          = Salt     { unSalt :: ByteString } deriving (Show, Eq)+newtype PassHash      = PassHash { unHash :: ByteString } deriving (Show, Eq)+newtype EncryptedPass =+    EncryptedPass { unEncryptedPass  :: ByteString } deriving (Show, Eq)++------------------------------------------------------------------------------+-- $params+--+-- Scrypt takes three tuning parameters: @N@, @r@ and @p@. They affect running+-- time and memory usage:+--+-- /Memory usage/ is approximately @128*r*N@ bytes. Note that the+-- 'scryptParams' function takes @log_2(N)@ as a parameter. As an example,+-- the 'defaultParams'+--+-- >   log_2(N) = 14, r = 8 and p = 1+--+-- lead to 'scrypt' using @128 * 8 * 2^14 = 16M@ bytes of memory.+--+-- /Running time/ is proportional to all of @N@, @r@ and @p@. Since it's+-- influence on memory usage is small, @p@ can be used to independently tune+-- the running time.+--++-- |Encapsulates the three tuning parameters to the 'scrypt' function: @N@,+-- @r@ and @p@ (see above).+--+data ScryptParams = Params { logN, r, p, bufLen :: Integer} deriving (Eq)++instance Show ScryptParams where+    show Params{..} = concat [ "ScryptParams "+        , "{ logN=", show logN+        , ", r="   , show r+        , ", p="   , show p+        , " }"+        ]++-- |Constructor function for the 'ScryptParams' data type+--+scryptParams+    :: Integer+    -- ^ @log_2(N)@. Scrypt's @N@ parameter must be a power of two greater+    --   than one, thus it's logarithm to base two must be greater than zero.+    --   @128*r*N@ must be smaller than the available memory address space.+    -> Integer+    -- ^ The parameter @r@, must be greater than zero.+    -> Integer+    -- ^ The parameter @p@, must be greater than zero. @r@ and @p@+    --   must satisfy @r*p < 2^30@.+    -> Maybe ScryptParams+    -- ^ Returns 'Just' the parameter object for valid arguments,+    --   otherwise 'Nothing'.+    --+scryptParams logN r p | valid     = Just ps+                      | otherwise = Nothing+  where+    ps    = Params { logN, r, p, bufLen = 64 }+    valid = and [ logN > 0, r > 0, p > 0+                , r*p < 2^(30 :: Int)+                , bufLen ps <= 2^(32 :: Int)-1 * 32+                -- allocation fits into (virtual) memory+                , 128*r*2^logN <= fromIntegral (maxBound :: CSize)+                ]++-- |Default parameters as recommended in the scrypt paper:+--+--  >   N = 2^14, r = 8, p = 1+--+--  Equivalent to @'fromJust' ('scryptParams' 14 8 1)@.+--+defaultParams :: ScryptParams+defaultParams = fromJust (scryptParams 14 8 1)++------------------------------------------------------------------------------+-- $password-storage+--+-- To allow storing encrypted passwords conveniently in a single database+-- column, the password storage API provides the data type 'EncryptedPass'. It+-- combines a 'Pass' as well as the 'Salt' and 'ScryptParams' used to compute+-- it into a single 'ByteString', separated by pipe (\"|\") characters. The+-- 'Salt' and 'PassHash' are base64-encoded. Storing the 'ScryptParams' with +-- the password allows to gradually strengthen password encryption in case of+-- changing security requirements.+--+-- A usage example is given below, showing encryption, verification and+-- changing 'ScryptParams':+--+-- > >>> encrypted <- encryptPass defaultParams (Pass "secret")+-- > >>> print encrypted+-- > EncryptedPass {unEncryptedPass = "14|8|1|Wn5x[SNIP]nM=|Zl+p[SNIP]g=="}+-- > >>> print $ verifyPass defaultParams (Pass "secret") encrypted+-- > (True,Nothing)+-- > >>> print $ verifyPass defaultParams (Pass "wrong") encrypted+-- > (False,Nothing)+-- > >>> let newParams = fromJust $ scryptParams 16 8 1+-- > >>> print $ verifyPass newParams (Pass "secret") encrypted+-- > (True,Just (EncryptedPass {unEncryptedPass = "16|8|1|Wn5x[SNIP]nM=|ZmWw[SNIP]Q=="}))+--++combine :: ScryptParams -> Salt -> PassHash -> EncryptedPass+combine Params{..} (Salt salt) (PassHash passHash) =+    EncryptedPass $ intercalate "|"+        [ showBS logN, showBS r, showBS p+        , Base64.encode salt, Base64.encode passHash]+  where+    showBS = pack . show++separate :: EncryptedPass -> Maybe (ScryptParams, Salt, PassHash)+separate = go . split '|' . unEncryptedPass+  where+    go [logN', r', p', salt', hash'] = do+        [salt, hash] <- mapM decodeBase64 [salt', hash']+        [logN, r, p] <- mapM (fmap fst . readInteger) [logN', r', p']+        params       <- scryptParams logN r p+        return (params, Salt salt, PassHash hash)+    go _         = Nothing+    decodeBase64 = either (const Nothing) Just . Base64.decode++-- |Encrypt the password with the given parameters and a random 32-byte salt.+-- The salt is read from @\/dev\/urandom@ on Unix systems or @CryptoAPI@ on+-- Windows.+--+encryptPass :: ScryptParams -> Pass -> IO EncryptedPass+encryptPass params pass = do+    salt <- Salt <$> getEntropy 32+    return $ combine params salt (scrypt params salt pass)++-- |Equivalent to @encryptPass defaultParams@.+--+encryptPass' :: Pass -> IO EncryptedPass+encryptPass' = encryptPass defaultParams++-- |Verify a 'Pass' against an 'EncryptedPass'. The function also takes+--  'ScryptParams' meeting your current security requirements. In case the+--  'EncryptedPass' was generated with different parameters, the function+--  returns an updated 'EncryptedPass', generated with the given +--  'ScryptParams'. The 'Salt' is kept from the given 'EncryptedPass'.+--+verifyPass+    :: ScryptParams+    -- ^ Parameters to use for updating the 'EncryptedPass'.+    -> Pass+    -- ^ The candidate 'Pass'.+    -> EncryptedPass+    -- ^ The 'EncryptedPass' to check against.+    -> (Bool, Maybe EncryptedPass)+    -- ^ Returns a pair of+    --+    --     * 'Bool' indicating verification success or failure.+    --+    --     * 'Just' a /new/ 'EncryptedPass' if the given 'ScryptParams' are+    --      different from those encapsulated in the /given/ 'EncryptedPass',+    --      otherwise 'Nothing'.+    --+verifyPass newParams candidate encrypted =+    maybe (False, Nothing) verify (separate encrypted)+  where+    verify (params,salt,hash) =+        let valid   = scrypt params salt candidate == hash+            newHash = scrypt newParams salt candidate+            newEncr = if not valid || params == newParams+                        then Nothing+                        else Just (combine newParams salt newHash)+        in (valid, newEncr)++-- |Check the 'Pass' against the 'EncryptedPass', using the 'ScryptParams'+--  encapsulated in the 'EncryptedPass'.+--+verifyPass' :: Pass -> EncryptedPass -> Bool+-- We never evaluate an eventual new 'EncryptedPass' from 'verifyPass', so it is+-- safe to pass 'undefined' to verifyPass.+verifyPass' pass encrypted = fst $ verifyPass undefined pass encrypted++------------------------------------------------------------------------------+-- $low-level+--+-- Bindings to a fast C implementation of 'scrypt'. For password storage,+-- consider using the more convenient higher-level API above.+--++-- |Calculates a 64-byte hash from the given password, salt and parameters.+--+scrypt :: ScryptParams -> Salt -> Pass -> PassHash+scrypt Params{..} (Salt salt) (Pass pass) =+    PassHash <$> unsafePerformIO $+        useAsCStringLen salt $ \(saltPtr, saltLen) ->+        useAsCStringLen pass $ \(passPtr, passLen) ->+        allocaBytes (fromIntegral bufLen) $ \bufPtr -> do+            throwErrnoIfMinus1_ "crypto_scrypt" $ crypto_scrypt+                (castPtr passPtr) (fromIntegral passLen)+                (castPtr saltPtr) (fromIntegral saltLen)+                (2^logN) (fromIntegral r) (fromIntegral p)+                bufPtr (fromIntegral bufLen)+            packCStringLen (castPtr bufPtr, fromIntegral bufLen)++foreign import ccall unsafe crypto_scrypt+    :: Ptr Word8 -> CSize         -- password+    -> Ptr Word8 -> CSize         -- salt+    -> Word64 -> Word32 -> Word32 -- N, r, p+    -> Ptr Word8 -> CSize         -- result buffer+    -> IO CInt++-- |Note the prime symbol (\'). Calls 'scrypt' with 'defaultParams'.+--+scrypt' :: Salt -> Pass -> PassHash+scrypt' = scrypt defaultParams
+ test/Test.hs view
@@ -0,0 +1,119 @@+{-# LANGUAGE OverloadedStrings #-}++module Main where++import Control.Applicative ((<$>))+import Crypto.Scrypt+import Data.ByteString (pack)+import Data.Maybe+import Test.Framework (Test, defaultMain, testGroup)+import Test.Framework.Providers.HUnit (testCase)+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.HUnit ((@=?))+import Test.QuickCheck+import Test.QuickCheck.Property (morallyDubiousIOProperty)++instance Arbitrary ScryptParams where+    arbitrary = do+        logN <- elements [1..14]+        r    <- elements [1..8]+        p    <- elements [1..2]+        frequency+            [(  1, return defaultParams)+            ,(100, return . fromJust $ scryptParams logN r p)+            ]++instance Arbitrary Pass where+    arbitrary = Pass . pack <$> listOf (arbitrary `suchThat` (/= 0))+++main :: IO ()+main = defaultMain+    [ testGroup "Test Vectors" testVectors+    , testGroup "Properties"+        [ testProperty "wrong pass is invalid" prop_WrongPassNotValid+        , testProperty "encrypt/verify" prop_EncryptVerify+        , testProperty "encrypt/verify'" prop_EncryptVerify'+        , testProperty "new params ==> new encryption" prop_NewParamsNewEncr+        ]+    ]++prop_WrongPassNotValid :: Pass -> Pass -> ScryptParams -> Property+prop_WrongPassNotValid pass candidate params =+    pass /= candidate ==> morallyDubiousIOProperty $ do+        encr <- encryptPass params pass+        let (valid, newEncr) = verifyPass params candidate encr+        return $ not valid && isNothing newEncr++prop_EncryptVerify :: Pass -> ScryptParams -> Property+prop_EncryptVerify pass params =+    morallyDubiousIOProperty $ do+        encr <- encryptPass params pass+        let (valid, newEncr) = verifyPass params pass encr+        return $ valid && isNothing newEncr++prop_EncryptVerify' :: Pass -> ScryptParams -> Property+prop_EncryptVerify' pass params =+    morallyDubiousIOProperty $ do+        encr <- encryptPass params pass+        return $ verifyPass' pass encr++prop_NewParamsNewEncr :: Pass -> ScryptParams -> ScryptParams -> Property+prop_NewParamsNewEncr pass oldParams newParams =+    oldParams /= newParams ==> morallyDubiousIOProperty $ do+        encr <- encryptPass oldParams pass        +        let (valid,newEncr) = verifyPass newParams pass encr+        return $ valid && isJust newEncr && fromJust newEncr /= encr++-- |Test vectors from the scrypt paper.+--+testVectors :: [Test]+testVectors = map toTestCase vecs+  where+    toTestCase (pass, salt, logN, r, p, dk) =+        let params = fromJust $ scryptParams logN r p+        in testCase (unwords ["vec:", show pass, show salt, show params]) $+                PassHash (pack dk) @=? scrypt params (Salt salt) (Pass pass)+    +    vecs =+        [( "", "", 4, 1, 1,+            [ 0x77, 0xd6, 0x57, 0x62, 0x38, 0x65, 0x7b, 0x20+            , 0x3b, 0x19, 0xca, 0x42, 0xc1, 0x8a, 0x04, 0x97+            , 0xf1, 0x6b, 0x48, 0x44, 0xe3, 0x07, 0x4a, 0xe8+            , 0xdf, 0xdf, 0xfa, 0x3f, 0xed, 0xe2, 0x14, 0x42+            , 0xfc, 0xd0, 0x06, 0x9d, 0xed, 0x09, 0x48, 0xf8+            , 0x32, 0x6a, 0x75, 0x3a, 0x0f, 0xc8, 0x1f, 0x17+            , 0xe8, 0xd3, 0xe0, 0xfb, 0x2e, 0x0d, 0x36, 0x28+            , 0xcf, 0x35, 0xe2, 0x0c, 0x38, 0xd1, 0x89, 0x06+            ])+        ,("password", "NaCl", 10, 8, 16,+            [ 0xfd, 0xba, 0xbe, 0x1c, 0x9d, 0x34, 0x72, 0x00+            , 0x78, 0x56, 0xe7, 0x19, 0x0d, 0x01, 0xe9, 0xfe+            , 0x7c, 0x6a, 0xd7, 0xcb, 0xc8, 0x23, 0x78, 0x30+            , 0xe7, 0x73, 0x76, 0x63, 0x4b, 0x37, 0x31, 0x62+            , 0x2e, 0xaf, 0x30, 0xd9, 0x2e, 0x22, 0xa3, 0x88+            , 0x6f, 0xf1, 0x09, 0x27, 0x9d, 0x98, 0x30, 0xda+            , 0xc7, 0x27, 0xaf, 0xb9, 0x4a, 0x83, 0xee, 0x6d+            , 0x83, 0x60, 0xcb, 0xdf, 0xa2, 0xcc, 0x06, 0x40+            ])+        ,("pleaseletmein", "SodiumChloride", 14, 8, 1,+            [ 0x70, 0x23, 0xbd, 0xcb, 0x3a, 0xfd, 0x73, 0x48+            , 0x46, 0x1c, 0x06, 0xcd, 0x81, 0xfd, 0x38, 0xeb+            , 0xfd, 0xa8, 0xfb, 0xba, 0x90, 0x4f, 0x8e, 0x3e+            , 0xa9, 0xb5, 0x43, 0xf6, 0x54, 0x5d, 0xa1, 0xf2+            , 0xd5, 0x43, 0x29, 0x55, 0x61, 0x3f, 0x0f, 0xcf+            , 0x62, 0xd4, 0x97, 0x05, 0x24, 0x2a, 0x9a, 0xf9+            , 0xe6, 0x1e, 0x85, 0xdc, 0x0d, 0x65, 0x1e, 0x40+            , 0xdf, 0xcf, 0x01, 0x7b, 0x45, 0x57, 0x58, 0x87+            ])+        -- ,("pleaseletmein", "SodiumChloride", 20, 8, 1,+        --      [ 0x21, 0x01, 0xcb, 0x9b, 0x6a, 0x51, 0x1a, 0xae+        --      , 0xad, 0xdb, 0xbe, 0x09, 0xcf, 0x70, 0xf8, 0x81+        --      , 0xec, 0x56, 0x8d, 0x57, 0x4a, 0x2f, 0xfd, 0x4d+        --      , 0xab, 0xe5, 0xee, 0x98, 0x20, 0xad, 0xaa, 0x47+        --      , 0x8e, 0x56, 0xfd, 0x8f, 0x4b, 0xa5, 0xd0, 0x9f+        --      , 0xfa, 0x1c, 0x6d, 0x92, 0x7c, 0x40, 0xf4, 0xc3+        --      , 0x37, 0x30, 0x40, 0x49, 0xe8, 0xa9, 0x52, 0xfb+        --      , 0xcb, 0xf4, 0x5c, 0x6f, 0xa7, 0x7a, 0x41, 0xa4+        --      ])+        ]