packages feed

scrypt 0.2.1 → 0.3.0

raw patch · 2 files changed

+177/−47 lines, 2 filesdep +base64-bytestring

Dependencies added: base64-bytestring

Files

Crypto/Scrypt.hs view
@@ -1,45 +1,64 @@-{-# LANGUAGE ForeignFunctionInterface, RecordWildCards, NamedFieldPuns #-}+{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings,+    RecordWildCards, NamedFieldPuns #-}  -- |Scrypt is a sequential memory-hard key derivation function. This module---  provides bindings to a fast C implementation of scrypt, written by Colin---  Percival. For more information see <http://www.tarsnap.com/scrypt.html>.-module Crypto.Scrypt ( +--  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-     ScryptParams, params-    -- * The @scrypt@ key derivation function-    , Pass(..), Salt(..), PassHash(..)-    , scrypt, scrypt', getSalt+    -- $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 Data.ByteString (ByteString, useAsCStringLen, packCStringLen, hGet)+import Control.Applicative+import Data.ByteString.Base64 (encode)+import qualified Data.ByteString.Base64 as Base64+import Data.ByteString.Char8 hiding (map, concat) import Data.Maybe import Foreign import Foreign.C import System.IO  -newtype Pass = Pass { unPass :: ByteString } deriving (Show)-newtype Salt = Salt { unSalt :: ByteString } deriving (Show)-newtype PassHash = PassHash { unHash :: ByteString } deriving (Show,Eq)+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) --- |Encapsulates the three tuning parameters to the 'scrypt' function: @N@,---  @r@ and @p@. The parameters affect running time and memory usage:+------------------------------------------------------------------------------+-- $params -----  /Memory usage/ is approximately @128*r*N@ bytes. Note that the---  'params' function takes @log_2(N)@ as a parameter. As an example, the---  default parameters used by 'scrypt''---  ---  @   log_2(N) = 14, r = 8 and p = 1@+-- Scrypt takes three tuning parameters: @N@, @r@ and @p@. The parameters+-- 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@. However---  @p@ only as an insignificant influence on memory usage an can thus be---  used to tune the running time of 'scrypt'.+--  @p@ only has an insignificant influence on memory usage an can thus be+--  used to independently tune the running time of 'scrypt'. ---data ScryptParams = Params { logN, r, p, bufLen :: Integer} +-- |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@@ -49,27 +68,145 @@         ]  -- |Constructor function for the 'ScryptParams' data type-params :: 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. -       -> 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'.-params logN r p | valid     = Just ps-                | otherwise = Nothing+--+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, encode salt, 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@.+--+encryptPass :: ScryptParams -> Pass -> IO EncryptedPass+encryptPass params pass = do+    salt <- Salt <$> withBinaryFile "/dev/urandom" ReadMode (`hGet` 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)++-- |Equivalent to @verifyPass defaultParams@.+--+verifyPass' :: Pass -> EncryptedPass -> (Bool, Maybe EncryptedPass)+verifyPass' = verifyPass defaultParams++------------------------------------------------------------------------------+-- $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 $@@ -90,15 +227,7 @@     -> Ptr Word8 -> CSize         -- result buffer     -> IO CInt --- |Note the prime symbol (\'). Calls 'scrypt' with default parameters as---  recommended in the scrypt paper:------  @   N = 2^14, r = 8, p = 1 @+-- |Note the prime symbol (\'). Calls 'scrypt' with 'defaultParams'. -----  Equivalent to @'scrypt' ('fromJust' ('params' 14 8 1))@. scrypt' :: Salt -> Pass -> PassHash-scrypt' = scrypt $ fromJust (params 14 8 1)---- |Reads a 32-byte random salt from @\/dev\/urandom@.-getSalt :: IO Salt-getSalt = Salt <$> withBinaryFile "/dev/urandom" ReadMode (flip hGet 32)+scrypt' = scrypt $ fromJust (scryptParams 14 8 1)
scrypt.cabal view
@@ -1,5 +1,5 @@ name:           scrypt-version:        0.2.1+version:        0.3.0 license:        BSD3 license-file:   LICENSE category:       Cryptography@@ -35,6 +35,7 @@      build-depends:         base == 4.*,+        base64-bytestring == 0.1.*,         bytestring == 0.9.*      ghc-options: -Wall