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 +0/−235
- scrypt.cabal +24/−9
- src/Crypto/Scrypt.hs +238/−0
- test/Test.hs +119/−0
− 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+ -- ])+ ]