packages feed

crypto-sodium 0.0.2.0 → 0.0.3.0

raw patch · 14 files changed

+987/−31 lines, 14 filesdep +base16-bytestringdep +cerealdep +hedgehogdep −gdpdep ~libsodiumdep ~memoryPVP ok

version bump matches the API change (PVP)

Dependencies added: base16-bytestring, cereal, hedgehog, tasty-hedgehog

Dependencies removed: gdp

Dependency ranges changed: libsodium, memory

API changes (from Hackage documentation)

+ Crypto.Encrypt.Box: create :: (ByteArrayAccess nonceBytes, ByteArrayAccess ptBytes, ByteArray ctBytes) => PublicKey -> SecretKey -> Nonce nonceBytes -> ptBytes -> ctBytes
+ Crypto.Encrypt.Box: keypair :: IO (PublicKey, SecretKey)
+ Crypto.Encrypt.Box: open :: (ByteArrayAccess nonceBytes, ByteArray ptBytes, ByteArrayAccess ctBytes) => SecretKey -> PublicKey -> Nonce nonceBytes -> ctBytes -> Maybe ptBytes
+ Crypto.Encrypt.Box: toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)
+ Crypto.Encrypt.Box: toPublicKey :: ByteString -> Maybe PublicKey
+ Crypto.Encrypt.Box: toSecretKey :: ScrubbedBytes -> Maybe SecretKey
+ Crypto.Encrypt.Box: type Nonce a = SizedByteArray CRYPTO_BOX_NONCEBYTES a
+ Crypto.Encrypt.Box: type PublicKey = SizedByteArray CRYPTO_BOX_PUBLICKEYBYTES ByteString
+ Crypto.Encrypt.Box: type SecretKey = SizedByteArray CRYPTO_BOX_SECRETKEYBYTES ScrubbedBytes
+ Crypto.Encrypt.Secretbox: create :: (ByteArrayAccess keyBytes, ByteArrayAccess nonceBytes, ByteArrayAccess ptBytes, ByteArray ctBytes) => Key keyBytes -> Nonce nonceBytes -> ptBytes -> ctBytes
+ Crypto.Encrypt.Secretbox: open :: (ByteArrayAccess keyBytes, ByteArrayAccess nonceBytes, ByteArray ptBytes, ByteArrayAccess ctBytes) => Key keyBytes -> Nonce nonceBytes -> ctBytes -> Maybe ptBytes
+ Crypto.Encrypt.Secretbox: toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)
+ Crypto.Encrypt.Secretbox: toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)
+ Crypto.Encrypt.Secretbox: type Key a = SizedByteArray CRYPTO_SECRETBOX_KEYBYTES a
+ Crypto.Encrypt.Secretbox: type Nonce a = SizedByteArray CRYPTO_SECRETBOX_NONCEBYTES a
+ Crypto.Key: Params :: !Word64 -> !Word64 -> Params
+ Crypto.Key: [memLimit] :: Params -> !Word64
+ Crypto.Key: [opsLimit] :: Params -> !Word64
+ Crypto.Key: data Params
+ Crypto.Key: derive :: forall key n passwd. (ByteArrayAccess passwd, ByteArrayN n key, key !>=! passwd, CRYPTO_PWHASH_BYTES_MIN <= n, n <= CRYPTO_PWHASH_BYTES_MAX) => Params -> passwd -> IO (Maybe (key, DerivationSlip))
+ Crypto.Key: generate :: KnownNat n => IO (SizedByteArray n ScrubbedBytes)
+ Crypto.Key: rederive :: forall key n passwd. (ByteArrayAccess passwd, ByteArrayN n key, key !>=! passwd, CRYPTO_PWHASH_BYTES_MIN <= n, n <= CRYPTO_PWHASH_BYTES_MAX) => DerivationSlip -> passwd -> Maybe key
+ Crypto.Key: type DerivationSlip = ByteString
+ Crypto.Key: type family a !>=! b :: Constraint
+ Crypto.Key.Internal: DerivationSlipData :: !Params -> !Salt ByteString -> DerivationSlipData
+ Crypto.Key.Internal: Params :: !Word64 -> !Word64 -> Params
+ Crypto.Key.Internal: [memLimit] :: Params -> !Word64
+ Crypto.Key.Internal: [nonce] :: DerivationSlipData -> !Salt ByteString
+ Crypto.Key.Internal: [opsLimit] :: Params -> !Word64
+ Crypto.Key.Internal: [params] :: DerivationSlipData -> !Params
+ Crypto.Key.Internal: data DerivationSlipData
+ Crypto.Key.Internal: data Params
+ Crypto.Key.Internal: derivationSlipDecode :: DerivationSlip -> Maybe DerivationSlipData
+ Crypto.Key.Internal: derivationSlipEncode :: DerivationSlipData -> DerivationSlip
+ Crypto.Key.Internal: derive :: (ByteArrayAccess passwd, ByteArrayN n key, CRYPTO_PWHASH_BYTES_MIN <= n, n <= CRYPTO_PWHASH_BYTES_MAX) => Params -> passwd -> IO (Maybe (key, DerivationSlip))
+ Crypto.Key.Internal: instance Data.Serialize.Serialize Crypto.Key.Internal.DerivationSlipData
+ Crypto.Key.Internal: instance GHC.Classes.Eq Crypto.Key.Internal.DerivationSlipData
+ Crypto.Key.Internal: instance GHC.Show.Show Crypto.Key.Internal.DerivationSlipData
+ Crypto.Key.Internal: rederive :: (ByteArrayAccess passwd, ByteArrayN n key, CRYPTO_PWHASH_BYTES_MIN <= n, n <= CRYPTO_PWHASH_BYTES_MAX) => DerivationSlip -> passwd -> IO (Maybe key)
+ Crypto.Key.Internal: type DerivationSlip = ByteString
+ Crypto.Pwhash.Internal: Argon2i_1_3 :: Algorithm
+ Crypto.Pwhash.Internal: Argon2id_1_3 :: Algorithm
+ Crypto.Pwhash.Internal: Params :: !Word64 -> !Word64 -> Params
+ Crypto.Pwhash.Internal: [memLimit] :: Params -> !Word64
+ Crypto.Pwhash.Internal: [opsLimit] :: Params -> !Word64
+ Crypto.Pwhash.Internal: data Algorithm
+ Crypto.Pwhash.Internal: data Params
+ Crypto.Pwhash.Internal: instance GHC.Classes.Eq Crypto.Pwhash.Internal.Algorithm
+ Crypto.Pwhash.Internal: instance GHC.Classes.Eq Crypto.Pwhash.Internal.Params
+ Crypto.Pwhash.Internal: instance GHC.Classes.Ord Crypto.Pwhash.Internal.Algorithm
+ Crypto.Pwhash.Internal: instance GHC.Classes.Ord Crypto.Pwhash.Internal.Params
+ Crypto.Pwhash.Internal: instance GHC.Show.Show Crypto.Pwhash.Internal.Algorithm
+ Crypto.Pwhash.Internal: instance GHC.Show.Show Crypto.Pwhash.Internal.Params
+ Crypto.Pwhash.Internal: pwhash :: forall passwd salt n hash. (ByteArrayAccess passwd, ByteArrayAccess salt, ByteArrayN n hash, CRYPTO_PWHASH_BYTES_MIN <= n, n <= CRYPTO_PWHASH_BYTES_MAX) => Algorithm -> Params -> passwd -> Salt salt -> IO (Maybe hash)
+ Crypto.Pwhash.Internal: type Salt a = SizedByteArray CRYPTO_PWHASH_SALTBYTES a

Files

CHANGELOG.md view
@@ -6,3 +6,4 @@  * `sodiumInit` * Random bytes generation: `Crypto.Random`+* Key derivation: `Crypto.Key.derive` and `Crypto.Key.rederive`
crypto-sodium.cabal view
@@ -1,13 +1,13 @@-cabal-version: 1.22+cabal-version: 1.18  -- This file has been generated from package.yaml by hpack version 0.33.0. -- -- see: https://github.com/sol/hpack ----- hash: 745d63b84deb20c2dc71acc5006fea4028aa386b595bb6e1d9016deba28aeca5+-- hash: 2d2774b6f1a9692b774ae9bb3b592030db69473bc4e1ee2aa080184a78ce4a10  name:           crypto-sodium-version:        0.0.2.0+version:        0.0.3.0 synopsis:       Easy-and-safe-to-use high-level cryptography based on Sodium description:    This is a collection of high-level cryptographic primitives based on                 <https://libsodium.io/ Sodium>, spiced up with extra type-safety@@ -35,30 +35,22 @@                 .                 = How                 .-                == Secret-key cryptography-                .-                * Authenticated encryption: "Crypto.Secretbox"-                * Encryption: "Crypto.Stream"-                * Authentication: "Crypto.Auth"+                == Library initialisation                 .+                * "Crypto.Init"                 .-                = Thread-safety #threadSafety#+                == Secret-key cryptography                 .-                Some of the Sodium (and NaCl) functions (those that generate random data)-                are not thread-safe. All these functions are explicitly marked as such-                in their Haddock documentation.+                * Authenticated symmetric-key encryption: "Crypto.Encrypt.Secretbox"                 .-                Calling 'sodiumInit' before they are used makes them thread-safe, see-                "Crypto.Init".+                == Public-key cryptography                 .-                = Performance+                * Authenticated public-key encryption: "Crypto.Encrypt.Box"                 .-                Sodium contains multiple implementations of the primitives it provides.-                There are generic implementations, that are used by default, and-                multiple alternatives optimised for various platforms.+                == Additional primitives                 .-                'sodiumInit' will quickly benchmark all available implementations and choose-                the bests ones for each primitive, see "Crypto.Init".+                * Key derivation and generation: "Crypto.Key"+                * Cryptographically-secure random: "Crypto.Random" category:       Cryptography homepage:       https://github.com/serokell/haskell-crypto#readme bug-reports:    https://github.com/serokell/haskell-crypto/issues@@ -78,23 +70,26 @@  library   exposed-modules:+      Crypto.Encrypt.Box+      Crypto.Encrypt.Secretbox       Crypto.Init+      Crypto.Key+      Crypto.Key.Internal+      Crypto.Pwhash.Internal       Crypto.Random   other-modules:       Paths_crypto_sodium-  reexported-modules:-      NaCl:Crypto.Secretbox   hs-source-dirs:       lib-  default-extensions: DataKinds FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving KindSignatures LambdaCase MultiParamTypeClasses NumericUnderscores OverloadedStrings PolyKinds ScopedTypeVariables+  default-extensions: DataKinds FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving KindSignatures LambdaCase MultiParamTypeClasses NamedFieldPuns NumericUnderscores OverloadedStrings PolyKinds ScopedTypeVariables TypeApplications   ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints   build-depends:       NaCl >=0.0.1.0 && <0.1     , base >=4.10 && <4.15     , bytestring >=0.9 && <0.11-    , gdp >=0.0.0.1 && <0.1+    , cereal >=0.1 && <0.6     , libsodium >=1.0.11 && <2-    , memory >=0.1 && <0.16+    , memory >=0.14.15 && <0.16     , safe-exceptions >=0.1 && <0.2   default-language: Haskell2010 @@ -102,21 +97,29 @@   type: exitcode-stdio-1.0   main-is: Test.hs   other-modules:+      Test.Crypto.Gen+      Test.Crypto.Key.Derivation+      Test.Crypto.Pwhash       Test.Crypto.Random+      Test.Crypto.Secretbox       Paths_crypto_sodium   hs-source-dirs:       test-  default-extensions: DataKinds FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving KindSignatures LambdaCase MultiParamTypeClasses NumericUnderscores OverloadedStrings PolyKinds ScopedTypeVariables+  default-extensions: DataKinds FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving KindSignatures LambdaCase MultiParamTypeClasses NamedFieldPuns NumericUnderscores OverloadedStrings PolyKinds ScopedTypeVariables TypeApplications   ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints   build-tool-depends:       tasty-discover:tasty-discover   build-depends:       HUnit     , base >=4.10 && <4.15+    , base16-bytestring     , bytestring >=0.9 && <0.11     , crypto-sodium-    , libsodium-    , memory+    , hedgehog+    , libsodium >=1.0.11 && <2+    , memory >=0.14.15 && <0.16+    , safe-exceptions >=0.1 && <0.2     , tasty+    , tasty-hedgehog     , tasty-hunit   default-language: Haskell2010
+ lib/Crypto/Encrypt/Box.hs view
@@ -0,0 +1,84 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- ! This module merely re-exports definitions from the corresponding+-- ! module in NaCl and alters the Haddock to make it more specific+-- ! to crypto-sodium. So, the docs should be kept more-or-less in sync.++-- | Public-key authenticated encryption.+--+-- It is best to import this module qualified:+--+-- @+-- import qualified Crypto.Encrypt.Box as Box+--+-- encrypted = Box.'create' pk sk nonce message+-- decrypted = Box.'open' pk sk nonce encrypted+-- @+--+-- A box is an abstraction from NaCl. One way to think about it+-- is to imagine that you are putting data into a box protected by+-- the receiver’s public key and signed by your private key. The+-- receive will then be able to 'open' it using their private key+-- and your public key.+--+-- Note that this means that you need to exchange your public keys+-- in advance. It might seem strange at first that the receiver+-- needs to know your public key too, but this is actually very important+-- as otherwise the receiver would not be able to have any guarantees+-- regarding the source or the integrity of the data.+module Crypto.Encrypt.Box+  (+  -- * Keys+    PublicKey+  , toPublicKey+  , SecretKey+  , toSecretKey+  , keypair++  -- * Nonce+  , Nonce+  , toNonce++  -- * Encryption/decryption+  , create+  , open+  ) where++import Data.ByteArray (ByteArray, ByteArrayAccess)+import Crypto.Box (Nonce, PublicKey, SecretKey, keypair, open, toNonce, toPublicKey, toSecretKey)++import qualified Crypto.Box as NaCl.Box+++-- | Encrypt a message.+--+-- @+-- encrypted = Box.create pk sk nonce message+-- @+--+-- *   @pk@ is the receiver’s public key, used for encryption.+--     @sk@ is the sender’s public key, used for authentication.+--+--     These are generated using 'keypair' and are supposed to be exchanged+--     in advance. Both parties need to know their own secret key and the other’s+--     public key.+--+-- *   @nonce@ is an extra noise that ensures that is required for security.+--     See "Crypto.Nonce" for how to work with it.+--+-- *   @message@ is the data you are encrypting.+--+-- This function adds authentication data, so if anyone modifies the cyphertext,+-- @open@ will refuse to decrypt it.+create+  ::  ( ByteArrayAccess nonceBytes+      , ByteArrayAccess ptBytes, ByteArray ctBytes+      )+  => PublicKey  -- ^ Receiver’s public key+  -> SecretKey  -- ^ Sender’s secret key+  -> Nonce nonceBytes  -- ^ Nonce+  -> ptBytes -- ^ Plaintext message+  -> ctBytes+create = NaCl.Box.create
+ lib/Crypto/Encrypt/Secretbox.hs view
@@ -0,0 +1,70 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- ! This module merely re-exports definitions from the corresponding+-- ! module in NaCl and alters the Haddock to make it more specific+-- ! to crypto-sodium. So, the docs should be kept more-or-less in sync.++-- | Symmetric authenticated encryption.+--+-- It is best to import this module qualified:+--+-- @+-- import qualified Crypto.Encrypt.Secretbox as Secretbox+--+-- encrypted = Secretbox.'create' key nonce message+-- decrypted = Secretbox.'open' key nonce encrypted+-- @+--+-- A secretbox is an abstraction from NaCl. One way to think about it+-- is to imagine that you are putting data into a box protected by a+-- secret key. You 'create' such a box first, store it somewhere+-- (it is just a sequence of bytes), and when you need it in the+-- future, you 'open' it using the same secret key.+module Crypto.Encrypt.Secretbox+  (+  -- * Keys+    Key+  , toKey++  -- * Nonce+  , Nonce+  , toNonce++  -- * Encryption/decryption+  , create+  , open+  ) where++import Crypto.Secretbox (Key, Nonce, open, toKey, toNonce)+import Data.ByteArray (ByteArray, ByteArrayAccess)++import qualified Crypto.Secretbox as NaCl.Secretbox+++-- | Encrypt a message.+--+-- @+-- encrypted = Secretbox.create key nonce message+-- @+--+-- *   @key@ is the secret key used for encryption. See "Crypto.Key" for how+--     to get one.+--+-- *   @nonce@ is an extra noise that ensures that is required for security.+--     See "Crypto.Nonce" for how to work with it.+--+-- *   @message@ is the data you are encrypting.+--+-- This function adds authentication data, so if anyone modifies the cyphertext,+-- @open@ will refuse to decrypt it.+create+  ::  ( ByteArrayAccess keyBytes, ByteArrayAccess nonceBytes+      , ByteArrayAccess ptBytes, ByteArray ctBytes+      )+  => Key keyBytes  -- ^ Secret key+  -> Nonce nonceBytes  -- ^ Nonce+  -> ptBytes -- ^ Plaintext message+  -> ctBytes+create = NaCl.Secretbox.create
lib/Crypto/Init.hs view
@@ -3,6 +3,23 @@ -- SPDX-License-Identifier: MPL-2.0  -- | Libsodium initialisation.++-- = Thread-safety #threadSafety#+--+-- Some of the Sodium (and NaCl) functions (those that generate random data)+-- are not thread-safe. All these functions are explicitly marked as such+-- in their Haddock documentation.+--+-- Calling 'sodiumInit' before they are used makes them thread-safe.+--+-- = Performance+--+-- Sodium contains multiple implementations of the primitives it provides.+-- There are generic implementations, that are used by default, and+-- multiple alternatives optimised for various platforms.+--+-- 'sodiumInit' will quickly benchmark all available implementations and choose+-- the best ones for each primitive. module Crypto.Init   ( sodiumInit   , SodiumInitException (..)
+ lib/Crypto/Key.hs view
@@ -0,0 +1,198 @@+{-# OPTIONS_GHC -Wno-redundant-constraints #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}++-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | This module gives different ways of obtaining secret keys.+--+-- = Key derivation (from a password)+--+-- Sometimes, instead of generating fresh random transient encryption keys,+-- you want an encryption key to be persistent and you don’t want to+-- store it anywhere – instead you ask the user to provide it.+-- Such a secret value known and entered by the user is usually called+-- a “password”.+--+-- However, passwords make terrible encryption keys because encryption keys:+--+-- 1. often need to have a specific exact length and+-- 2. need to be hard to guess (or brute-force).+--+-- Item 1 above can be easily ticked off by deriving the encryption key from+-- the password by applying a hash-function to it, however in order to+-- achieve 2 we need our “hash-function” to:+--+-- * be slow to compute (to make brute-forcing less feasible) and+-- * mix extra “noise” into the derivation process to make it harder+--   to pre-compute derived values in advance.+--+-- A construction that satisfies both requirements is called a+-- /key derivation function (KDF)/.+-- This module provides a convenient interface for deriving+-- secure keys from passwords by the way of one such KDF.+--+-- == Use+--+-- This module provides two functions: 'derive' and 'rederive'.+-- You can think of the entire process similar to how you set the password+-- on your account at some website once, and then use this password to log in.+--+-- When you derive a key for the first time (e.g. you ask the user to enter their+-- password twice, and then encrypt something), you use the 'derive' function,+-- which gives you the derived key and a /derivation slip/. The slip is not+-- secret, you can store it in plain text and, in fact, you /have to/ store it+-- in plaintext somewhere next to the encrypted data.+--+-- When you need to derive the key in the future (e.g. to decrypt some previously+-- encrypted data), you will need the user’s password (ask them) /and/ you+-- will need the original derivation slip, which you should have stored.+-- You pass these to 'rederive' and it will give you the same key.+--+-- @+-- import qualified Crypto.Key as Key+--+-- encrypt = do+--   password <- {- ask the user to enter their password -}+--   password2 <- {- ask the user to confirm their password -}+--   when (password /= password2 then) $ throwIO {- passwords do not match -}+--+--   let params = {- choose key derivation parameters -}+--   (key, slip) <- Key.derive params password+--+--   {- store slip (it is not secret) -}+--   {- encrypt data with key -}+--+-- decrypt = do+--   password <- {- ask the user to enter their password -}+--   slip <- {- get the stored slip -}+--+--   key <- Key.rederive slip password+--+--   {- decrypt data with key -}+-- @+--+-- = Random key generation+--+-- The 'random' function is great at generating new secure secret keys.+module Crypto.Key+  ( type (!>=!)++  -- * Key derivation+  , Params (..)+  , DerivationSlip+  , derive+  , rederive++  -- * Random key generation+  , generate+  ) where++import Data.ByteArray (ByteArrayAccess, ScrubbedBytes)+import Data.ByteArray.Sized (ByteArrayN, SizedByteArray)+import Data.Kind (Constraint)+import GHC.TypeLits (type (<=), KnownNat)+import System.IO.Unsafe (unsafeDupablePerformIO)++import qualified Libsodium as Na++import Crypto.Key.Internal (DerivationSlip, Params (..))++import qualified Crypto.Key.Internal as I+import qualified Crypto.Random+++-- | “At least as secure as”.+--+-- @a !>=! b@ means that the storage behind a is not less secure than b.+-- This is a little bit of an ad-hoc safety hack, which ensures that if+-- @b@ is stored in a securely allocated memory, then @a@ is stored in+-- memory allocated as securely, or more securely.+--+-- Here are our very ad-hoc rules:+--+-- * This relation is reflexive (@a@ is as secure as @a@ for any @a@).+-- * 'ScrubbedBytes' is more secure than anything.+-- * Everything else is equally (in)secure.+--+-- So, for example, if the original password is stored in @ScrubbedBytes@,+-- you will not be able to put the derived from it key into a @ByteString@,+-- because that would be less secure.+type family a !>=! b :: Constraint where+  a !>=! a = ()  -- reflexivity+  a !>=! ScrubbedBytes = LessSecureStorage a ScrubbedBytes+  a !>=! b = ()+class LessSecureStorage a b+++-- | Derive a key from a password using a secure KDF for the first time.+--+-- This function takes two arguments:+--+-- 1. key derivation parameters, which specify how slow the derivation process+-- will be (the slower you can afford the better for security),+--+-- 2. the user’s password to derive the key from.+--+-- See @libsodium@ documentation for how to determine 'Params'.+--+-- It returns the derived key and a /slip/ that you need to save in order to be+-- able to derive the same key from the same password in the future. The slip+-- is not secret, so you can store it in plaintext; just make sure you can+-- access it in the future, as you will need to provide it to 'rederive'.+--+-- It can derive a key of almost any length and the output length is encoded+-- in the type. There is an additional type-level restriction which forces+-- you to store the derived key in memory at least as securely as you+-- stored the password.+--+-- Note: This function is not thread-safe until Sodium is initialised.+-- See "Crypto.Init" for details.+derive+  ::  forall key n passwd.+      ( ByteArrayAccess passwd+      , ByteArrayN n key, key !>=! passwd+      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX+      )+  => I.Params -- ^ Derivation parameters.+  -> passwd  -- ^ Password to derive from.+  -> IO (Maybe (key, I.DerivationSlip))+derive = I.derive++-- | Reerive a key from a password using a secure KDF.+--+-- This function takes two arguments:+--+-- 1. A derivation slip previously returned by 'derive'.+--+-- 2. The user’s password.+--+-- This function is guaranteed to derive the same key from the same password+-- as long as the same derivation slip was provided.+--+-- See 'derive' for additional details.+rederive+  ::  forall key n passwd.+      ( ByteArrayAccess passwd+      , ByteArrayN n key, key !>=! passwd+      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX+      )+  => I.DerivationSlip -- ^ Original derivation slip.+  -> passwd  -- ^ Password to rederive from.+  -> Maybe key+rederive slip passwd =+  unsafeDupablePerformIO $ I.rederive slip passwd+  -- This IO is safe, because it is pure.+++-- | Generate a new secret key using a cryptographically-secure generator.+--+-- This is just a specialisation of @Crypto.Random.'generate'@ that stores+-- it in a secure memory location.+--+-- Note: This function is not thread-safe until Sodium is initialised.+-- See "Crypto.Init" for details.+generate :: KnownNat n => IO (SizedByteArray n ScrubbedBytes)+generate = Crypto.Random.generate
+ lib/Crypto/Key/Internal.hs view
@@ -0,0 +1,106 @@+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}++-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | Key derivation/generation internals.+module Crypto.Key.Internal+  ( Params (..)+  , DerivationSlip+  , derive+  , rederive++  , DerivationSlipData (..)+  , derivationSlipEncode+  , derivationSlipDecode+  ) where++import Control.Monad (when)+import Data.ByteArray (ByteArrayAccess)+import Data.ByteArray.Sized (ByteArrayN, sizedByteArray, unSizedByteArray)+import Data.ByteString (ByteString)+import Data.Serialize (Serialize (put, get), decode, encode)+import Data.Word (Word8)+import GHC.TypeLits (type (<=))++import qualified Libsodium as Na++import Crypto.Pwhash.Internal (Algorithm (Argon2id_1_3), Params (..), Salt, pwhash)+import Crypto.Random (generate)+++-- | Opaque bytes that contain the nonce and pwhash params.+type DerivationSlip = ByteString++-- | Data contained in a derivation slip.+--+-- This data type is used only internally within this module for+-- convenience. It is exported only for testing purposes.+--+-- Currently only one KDF is supported, so it is assumed implicitly,+-- however the actual binary encoding contains an identifier of the KDF+-- used (for forward-compatibility).+data DerivationSlipData = DerivationSlipData+  { params :: !Params+  , nonce :: !(Salt ByteString)+  }+  deriving (Eq, Show)++instance Serialize DerivationSlipData where+  put (DerivationSlipData Params{opsLimit, memLimit} nonce) = do+    put (1 :: Word8)  -- algorithm marker for forward-compatibility+    put opsLimit >> put memLimit+    put (unSizedByteArray nonce)+  get = do+    tag <- get @Word8+    when (tag /= 1) $ fail "Wrong algorithm parameters encoding tag"+    params <- Params <$> get <*> get+    mnonce <- sizedByteArray <$> get @ByteString+    case mnonce of+      Nothing -> fail "Unexpected nonce size"+      Just nonce -> pure $ DerivationSlipData params nonce+++-- | Encode derivation slip data into bytes.+derivationSlipEncode :: DerivationSlipData -> DerivationSlip+derivationSlipEncode = encode++-- | Decode derivation slip data from bytes.+derivationSlipDecode :: DerivationSlip -> Maybe DerivationSlipData+derivationSlipDecode bytes = case decode bytes of+  Right slip -> Just slip+  Left _ -> Nothing+++-- | Derive a key for the first time.+derive+  ::  ( ByteArrayAccess passwd+      , ByteArrayN n key+      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX+      )+  => Params+  -> passwd+  -> IO (Maybe (key, DerivationSlip))+derive params passwd = do+  nonce <- generate+  mkey <- pwhash Argon2id_1_3 params passwd nonce+  let slip = DerivationSlipData params nonce+  pure $ fmap (, derivationSlipEncode slip) mkey++-- | Derive the same key form the same password again.+rederive+  ::  ( ByteArrayAccess passwd+      , ByteArrayN n key+      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX+      )+  => DerivationSlip+  -> passwd+  -> IO (Maybe key)+rederive slip passwd =+  case derivationSlipDecode slip of+    Nothing -> pure Nothing+    Just (DerivationSlipData{params, nonce}) ->+      pwhash Argon2id_1_3 params passwd nonce
+ lib/Crypto/Pwhash/Internal.hs view
@@ -0,0 +1,82 @@+{-# OPTIONS_GHC -Wno-redundant-constraints #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}++-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | Tools for hashing passwords.+module Crypto.Pwhash.Internal+  ( Algorithm (..)+  , Params (..)+  , Salt++  , pwhash+  ) where++import Prelude hiding (length)++import Data.ByteArray (ByteArrayAccess, length, withByteArray)+import Data.ByteArray.Sized (ByteArrayN, SizedByteArray, allocRet)+import Data.Proxy (Proxy (Proxy))+import Data.Word (Word64)+import GHC.TypeLits (type (<=), natVal)+import Foreign.C.Types (CInt, CSize (CSize), CULLong (CULLong))++import qualified Libsodium as Na+++-- | Secure hashing algorithm.+data Algorithm+  = Argon2i_1_3 -- ^ Argon2i version 1.3+  | Argon2id_1_3 -- ^ Argon2id version 1.3+  deriving (Eq, Ord, Show)++algorithmToInt :: Algorithm -> CInt+algorithmToInt Argon2i_1_3 = Na.crypto_pwhash_alg_argon2i13+algorithmToInt Argon2id_1_3 = Na.crypto_pwhash_alg_argon2id13+++-- | Secure-hashing parameters.+data Params = Params+  { opsLimit :: !Word64  -- ^ Maximum amount of computation to perform.+  , memLimit :: !Word64  -- ^ Maximum amount of RAM (bytes) to use.+  }+  deriving (Eq, Ord, Show)+++-- | Salt used for password hashing.+--+-- This type is parametrised by the actual data type that contains+-- bytes. This can be, for example, a @ByteString@.+type Salt a = SizedByteArray Na.CRYPTO_PWHASH_SALTBYTES a+++-- | Securely hash a password.+--+-- This is @crypto_pwhash@, it can be used for key derivation.+pwhash+  ::  forall passwd salt n hash.+      ( ByteArrayAccess passwd, ByteArrayAccess salt+      , ByteArrayN n hash+      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX+      )+  => Algorithm  -- ^ Hashing algorithm.+  -> Params  -- ^ Hashing parameters.+  -> passwd  -- ^ Password to hash.+  -> Salt salt  -- ^ Hashing salt.+  -> IO (Maybe hash)+pwhash alg Params{opsLimit, memLimit} passwd salt = do+  (ret, hash) <-+    allocRet (Proxy :: Proxy n) $ \hashPtr ->+    withByteArray passwd $ \passwdPtr ->+    withByteArray salt $ \saltPtr -> do+      Na.crypto_pwhash hashPtr (fromIntegral $ natVal (Proxy :: Proxy n))+        passwdPtr (fromIntegral $ length passwd)+        saltPtr+        (CULLong opsLimit) (CSize memLimit) (algorithmToInt alg)+  if ret == 0 then+    pure $ Just hash+  else+    pure $ Nothing
lib/Crypto/Random.hs view
@@ -18,6 +18,9 @@ -- | Generate a sequence of cryptographically-secure renadom bytes. -- -- The output of this function is suitable to generate secret keys.+--+-- Note: This function is not thread-safe until Sodium is initialised.+-- See "Crypto.Init" for details. generate   :: forall ba n. (ByteArray ba, KnownNat n)   => IO (SizedByteArray n ba)
+ test/Test/Crypto/Gen.hs view
@@ -0,0 +1,36 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | Hedgehog generators of test data+module Test.Crypto.Gen where++import Hedgehog (Gen)++import Data.ByteString (ByteString)+import Data.ByteArray (ByteArray, ScrubbedBytes, convert)+import Data.ByteArray.Sized (SizedByteArray, sizedByteArray)+import Data.Maybe (fromJust)+import Data.Proxy (Proxy (Proxy))+import GHC.TypeLits (KnownNat, natVal)++import qualified Hedgehog.Gen as G+import qualified Hedgehog.Range as R+++-- | Generate a random sized byte array+sizedBytes+  :: forall n ba. (ByteArray ba, KnownNat n)+  => Gen (SizedByteArray n ba)+sizedBytes = fromJust . sizedByteArray . convert <$>+  G.bytes (R.singleton $ fromIntegral $ natVal (Proxy @n))++-- | Generate a random nonce of the right size.+nonce :: forall n. KnownNat n => Gen (SizedByteArray n ByteString)+nonce = sizedBytes++-- | Generate a random key of the right size.+--+-- THIS FUNCTION IS NOT SECURE AND IS ONLY SUITABLE FOR TESTS.+key :: forall n. KnownNat n => Gen (SizedByteArray n ScrubbedBytes)+key = sizedBytes
+ test/Test/Crypto/Key/Derivation.hs view
@@ -0,0 +1,49 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | Tests for our cool slip-based KDF+module Test.Crypto.Key.Derivation where++import Hedgehog (Gen, Property, (===), evalIO, forAll, property, tripping)++import qualified Hedgehog.Gen as G+import qualified Hedgehog.Range as R++import Data.ByteString (ByteString)+import Data.ByteArray.Sized (SizedByteArray)++import qualified Libsodium as Na++import qualified Test.Crypto.Gen as G++import Crypto.Key (Params (Params), derive, rederive)++import qualified Crypto.Key.Internal as KI+++genParams :: Gen Params+genParams = Params+  <$> G.integral+    (R.linear (fromIntegral Na.crypto_pwhash_opslimit_min) 10)+  <*> G.integral+    (R.linear (fromIntegral Na.crypto_pwhash_memlimit_min) (2 * 1024 * 1024))++genSlipData :: Gen KI.DerivationSlipData+genSlipData = KI.DerivationSlipData+  <$> genParams+  <*> G.nonce+++hprop_slip_encode_decode :: Property+hprop_slip_encode_decode = property $ do+    slipData <- forAll $ genSlipData+    tripping slipData KI.derivationSlipEncode KI.derivationSlipDecode++hprop_derive_rederive :: Property+hprop_derive_rederive = property $ do+    params <- forAll $ genParams+    passwd <- forAll $ G.bytes (R.linear 0 100)+    Just (key, slip) <- evalIO $+      derive @(SizedByteArray 64 ByteString) params passwd+    rederive slip passwd === Just key
+ test/Test/Crypto/Pwhash.hs view
@@ -0,0 +1,277 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}++module Test.Crypto.Pwhash where++import Test.HUnit ((@?=), Assertion)++import Data.ByteArray.Sized (sizedByteArray)+import Data.ByteString (ByteString)+import GHC.TypeLits (type (<=), KnownNat)++import qualified Data.ByteString as BS+import qualified Data.ByteString.Base16 as B16+import qualified Libsodium as Na++import Crypto.Pwhash.Internal (Algorithm (..), Params (Params), pwhash)+++pwhash_test_vector+  ::  forall n. -- ^ Output length.+      ( KnownNat n+      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX+      )+  => ByteString  -- ^ Expected hash.+  -> ByteString  -- ^ Password.+  -> ByteString  -- ^ Salt.+  -> Algorithm  -- ^ Hashing algorithm.+  -> Params  -- ^ Hashing params.+  -> Assertion+pwhash_test_vector hash passwd salt alg params = do+  let (hash', "") = B16.decode hash+  let (passwd', "") = B16.decode passwd+  let (salt', "") = B16.decode salt+  let Just salt'N = sizedByteArray (BS.take 16 salt')  -- Note:+    -- for some reason, the test vectors in the file are 32 bytes long,+    -- while the pwhash function needs a 16-byte salt :/+  let Just hash'N = sizedByteArray @n hash'+  result <- pwhash alg params passwd' salt'N+  result @?= Just hash'N+++-- Test vectors from+-- https://github.com/jedisct1/libsodium/blob/f911b56650b680ecfc5d32b11b090849fc2b5f92/test/default/pwhash_argon2id.c++unit_pwhash_argon2id_1 :: Assertion+unit_pwhash_argon2id_1 =+  pwhash_test_vector+    @155+    "18acec5d6507739f203d1f5d9f1d862f7c2cdac4f19d2bdff64487e60d969e3ced615337b9eec6ac4461c6ca07f0939741e57c24d0005c7ea171a0ee1e7348249d135b38f222e4dad7b9a033ed83f5ca27277393e316582033c74affe2566a2bea47f91f0fd9fe49ece7e1f79f3ad6e9b23e0277c8ecc4b313225748dd2a80f5679534a0700e246a79a49b3f74eb89ec6205fe1eeb941c73b1fcf1"+    (mconcat $+      [ "a347ae92bce9f80f6f595a4480fc9c2fe7e7d7148d371e9487d75f5c23008ffae0"+      , "65577a928febd9b1973a5a95073acdbeb6a030cfc0d79caa2dc5cd011cef02c08d"+      , "a232d76d52dfbca38ca8dcbd665b17d1665f7cf5fe59772ec909733b24de97d6f5"+      , "8d220b20c60d7c07ec1fd93c52c31020300c6c1facd77937a597c7a6"+      ]+    )+    "5541fbc995d5c197ba290346d2c559dedf405cf97e5f95482143202f9e74f5c2"+    Argon2id_1_3+    (Params 5 7256678)++unit_pwhash_argon2id_2 :: Assertion+unit_pwhash_argon2id_2 =+  pwhash_test_vector+    @250+    "26bab5f101560e48c711da4f05e81f5a3802b7a93d5155b9cab153069cc42b8e9f910bfead747652a0708d70e4de0bada37218bd203a1201c36b42f9a269b675b1f30cfc36f35a3030e9c7f57dfba0d341a974c1886f708c3e8297efbfe411bb9d51375264bd7c70d57a8a56fc9de2c1c97c08776803ec2cd0140bba8e61dc0f4ad3d3d1a89b4b710af81bfe35a0eea193e18a6da0f5ec05542c9eefc4584458e1da715611ba09617384748bd43b9bf1f3a6df4ecd091d0875e08d6e2fd8a5c7ce08904b5160cd38167b76ec76ef2d310049055a564da23d4ebd2b87e421cc33c401e12d5cd8d936c9baf75ebdfb557d342d2858fc781da31860"+    (mconcat $+      [ "e125cee61c8cb7778d9e5ad0a6f5d978ce9f84de213a8556d9ffe202020ab4a6ed"+      , "9074a4eb3416f9b168f137510f3a30b70b96cbfa219ff99f6c6eaffb15c06b60e0"+      , "0cc2890277f0fd3c622115772f7048adaebed86e"+      ]+    )+    "f1192dd5dc2368b9cd421338b22433455ee0a3699f9379a08b9650ea2c126f0d"+    Argon2id_1_3+    (Params 4 7849083)++unit_pwhash_argon2id_3 :: Assertion+unit_pwhash_argon2id_3 =+  pwhash_test_vector+    @249+    "6eb45e668582d63788ca8f6e930ca60b045a795fca987344f9a7a135aa3b5132b50a34a3864c26581f1f56dd0bcbfafbfa92cd9bff6b24a734cfe88f854aef4bda0a7983120f44936e8ff31d29728ac08ccce6f3f916b3c63962755c23a1fa9bb4e8823fc867bfd18f28980d94bc5874423ab7f96cc0ab78d8fa21fbd00cd3a1d96a73fa439ccc3fc4eab1590677b06cc78b0f674dfb680f23022fb902022dd8620803229c6ddf79a8156ccfce48bbd76c05ab670634f206e5b2e896230baa74a856964dbd8511acb71d75a1506766a125d8ce037f1db72086ebc3bccaefbd8cd9380167c2530386544ebfbeadbe237784d102bb92a10fd242"+    (mconcat $+      [ "92263cbf6ac376499f68a4289d3bb59e5a22335eba63a32e6410249155b956b6a3"+      , "b48d4a44906b18b897127300b375b8f834f1ceffc70880a885f47c33876717e392"+      , "be57f7da3ae58da4fd1f43daa7e44bb82d3717af4319349c24cd31e46d295856b0"+      , "441b6b289992a11ced1cc3bf3011604590244a3eb737ff221129215e4e4347f491"+      , "5d41292b5173d196eb9add693be5319fdadc242906178bb6c0286c9b6ca6012746"+      , "711f58c8c392016b2fdfc09c64f0f6b6ab7b"+      ]+    )+    "3b840e20e9555e9fb031c4ba1f1747ce25cc1d0ff664be676b9b4a90641ff194"+    Argon2id_1_3+    (Params 3 7994791)++-- unit_pwhash_argon2id_4 is skipped because it is a test for an incorrect output+-- size, which is impossible due to stronger types in this library++unit_pwhash_argon2id_5 :: Assertion+unit_pwhash_argon2id_5 =+  pwhash_test_vector+    @190+    "08d8cd330c57e1b4643241d05bb468ba4ee4e932cd0858816be9ef15360b27bbd06a87130ee92222be267a29b81f5ae8fe8613324cfc4832dc49387fd0602f1c57b4d0f3855db94fb7e12eb05f9a484aed4a4307abf586cd3d55c809bc081541e00b682772fb2066504ff935b8ebc551a2083882f874bc0fae68e56848ae34c91097c3bf0cca8e75c0797eef3efde3f75e005815018db3cf7c109a812264c4de69dcb22322dbbcfa447f5b00ecd1b04a7be1569c8e556adb7bba48adf81d"+    (mconcat $+      [ "4a857e2ee8aa9b6056f2424e84d24a72473378906ee04a46cb05311502d5250b82"+      , "ad86b83c8f20a23dbb74f6da60b0b6ecffd67134d45946ac8ebfb3064294bc097d"+      , "43ced68642bfb8bbbdd0f50b30118f5e"+      ]+    )+    "39d82eef32010b8b79cc5ba88ed539fbaba741100f2edbeca7cc171ffeabf258"+    Argon2id_1_3+    (Params 3 1432947)++unit_pwhash_argon2id_6 :: Assertion+unit_pwhash_argon2id_6 =+  pwhash_test_vector+    @178+    "d6e9d6cabd42fb9ba7162fe9b8e41d59d3c7034756cb460c9affe393308bd0225ce0371f2e6c3ca32aca2002bf2d3909c6b6e7dfc4a00e850ff4f570f8f749d4bb6f0091e554be67a9095ae1eefaa1a933316cbec3c2fd4a14a5b6941bda9b7eabd821d79abde2475a53af1a8571c7ee46460be415882e0b393f48c12f740a6a72cba9773000602e13b40d3dfa6ac1d4ec43a838b7e3e165fecad4b2498389e60a3ff9f0f8f4b9fca1126e64f49501e38690"+    (mconcat $+      [ "c7b09aec680e7b42fedd7fc792e78b2f6c1bea8f4a884320b648f81e8cf515e8ba"+      , "9dcfb11d43c4aae114c1734aa69ca82d44998365db9c93744fa28b63fd16000e82"+      , "61cbbe083e7e2da1e5f696bde0834fe53146d7e0e35e7de9920d041f5a5621aabe"+      , "02da3e2b09b405b77937efef3197bd5772e41fdb73fb5294478e45208063b5f58e"+      , "089dbeb6d6342a909c1307b3fff5fe2cf4da56bdae50848f"+      ]+    )+    "039c056d933b475032777edbaffac50f143f64c123329ed9cf59e3b65d3f43b6"+    Argon2id_1_3+    (Params 3 4886999)++unit_pwhash_argon2id_7 :: Assertion+unit_pwhash_argon2id_7 =+  pwhash_test_vector+    @231+    "7fb72409b0987f8190c3729710e98c3f80c5a8727d425fdcde7f3644d467fe973f5b5fee683bd3fce812cb9ae5e9921a2d06c2f1905e4e839692f2b934b682f11a2fe2b90482ea5dd234863516dba6f52dc0702d324ec77d860c2e181f84472bd7104fedce071ffa93c5309494ad51623d214447a7b2b1462dc7d5d55a1f6fd5b54ce024118d86f0c6489d16545aaa87b6689dad9f2fb47fda9894f8e12b87d978b483ccd4cc5fd9595cdc7a818452f915ce2f7df95ec12b1c72e3788d473441d884f9748eb14703c21b45d82fd667b85f5b2d98c13303b3fe76285531a826b6fc0fe8e3dddecf"+    (mconcat $+      [ "b540beb016a5366524d4605156493f9874514a5aa58818cd0c6dfffaa9e90205f1"+      , "7b"+      ]+    )+    "44071f6d181561670bda728d43fb79b443bb805afdebaf98622b5165e01b15fb"+    Argon2id_1_3+    (Params 1 1631659)++unit_pwhash_argon2id_8 :: Assertion+unit_pwhash_argon2id_8 =+  pwhash_test_vector+    @167+    "4e702bc5f891df884c6ddaa243aa846ce3c087fe930fef0f36b3c2be34164ccc295db509254743f18f947159c813bcd5dd8d94a3aec93bbe57605d1fad1aef1112687c3d4ef1cb329d21f1632f626818d766915d886e8d819e4b0b9c9307f4b6afc081e13b0cf31db382ff1bf05a16aac7af696336d75e99f82163e0f371e1d25c4add808e215697ad3f779a51a462f8bf52610af21fc69dba6b072606f2dabca7d4ae1d91d919"+    (mconcat $+      [ "a14975c26c088755a8b715ff2528d647cd343987fcf4aa25e7194a8417fb2b4b3f"+      , "7268da9f3182b4cfb22d138b2749d673a47ecc7525dd15a0a3c66046971784bb63"+      , "d7eae24cc84f2631712075a10e10a96b0e0ee67c43e01c423cb9c44e5371017e9c"+      , "496956b632158da3fe12addecb88912e6759bc37f9af2f45af72c5cae3b179ffb6"+      , "76a697de6ebe45cd4c16d4a9d642d29ddc0186a0a48cb6cd62bfc3dd229d313b30"+      , "1560971e740e2cf1f99a9a090a5b283f35475057e96d7064e2e0fc81984591068d"+      , "55a3b4169f22cccb0745a2689407ea1901a0a766eb99"+      ]+    )+    "3d968b2752b8838431165059319f3ff8910b7b8ecb54ea01d3f54769e9d98daf"+    Argon2id_1_3+    (Params 3 1784128)+++-- Test vectors from+-- https://github.com/jedisct1/libsodium/blob/f911b56650b680ecfc5d32b11b090849fc2b5f92/test/default/pwhash_argon2i.c++unit_pwhash_argon2i_1 :: Assertion+unit_pwhash_argon2i_1 =+  pwhash_test_vector+    @155+    "23b803c84eaa25f4b44634cc1e5e37792c53fcd9b1eb20f865329c68e09cbfa9f1968757901b383fce221afe27713f97914a041395bbe1fb70e079e5bed2c7145b1f6154046f5958e9b1b29055454e264d1f2231c316f26be2e3738e83a80315e9a0951ce4b137b52e7d5ee7b37f7d936dcee51362bcf792595e3c896ad5042734fc90c92cae572ce63ff659a2f7974a3bd730d04d525d253ccc38"+    (mconcat $+      [ "a347ae92bce9f80f6f595a4480fc9c2fe7e7d7148d371e9487d75f5c23008ffae0"+      , "65577a928febd9b1973a5a95073acdbeb6a030cfc0d79caa2dc5cd011cef02c08d"+      , "a232d76d52dfbca38ca8dcbd665b17d1665f7cf5fe59772ec909733b24de97d6f5"+      , "8d220b20c60d7c07ec1fd93c52c31020300c6c1facd77937a597c7a6"+      ]+    )+    "5541fbc995d5c197ba290346d2c559dedf405cf97e5f95482143202f9e74f5c2"+    Argon2i_1_3+    (Params 5 7256678)++unit_pwhash_argon2i_2 :: Assertion+unit_pwhash_argon2i_2 =+  pwhash_test_vector+    @250+    "0bb3769b064b9c43a9460476ab38c4a9a2470d55d4c992c6e723af895e4c07c09af41f22f90eab583a0c362d177f4677f212482fd145bfb9ac6211635e48461122bb49097b5fb0739d2cd22a39bf03d268e7495d4fd8d710aa156202f0a06e932ff513e6e7c76a4e98b6df5cf922f124791b1076ad904e6897271f5d7d24c5929e2a3b836d0f2f2697c2d758ee79bf1264f3fae65f3744e0f6d7d07ef6e8b35b70c0f88e9036325bfb24ac7f550351486da87aef10d6b0cb77d1cf6e31cf98399c6f241c605c6530dffb4764784f6c0b0bf601d4e4431e8b18dabdc3079c6e264302ade79f61cbd5497c95486340bb891a737223100be0429650"+    (mconcat $+      [ "e125cee61c8cb7778d9e5ad0a6f5d978ce9f84de213a8556d9ffe202020ab4a6ed"+      , "9074a4eb3416f9b168f137510f3a30b70b96cbfa219ff99f6c6eaffb15c06b60e0"+      , "0cc2890277f0fd3c622115772f7048adaebed86e"+      ]+    )+    "f1192dd5dc2368b9cd421338b22433455ee0a3699f9379a08b9650ea2c126f0d"+    Argon2i_1_3+    (Params 4 7849083)++unit_pwhash_argon2i_3 :: Assertion+unit_pwhash_argon2i_3 =+  pwhash_test_vector+    @249+    "e9aa073b0b872f15c083d1d7ce52c09f493b827ca78f13a06c1721b45b1e17b24c04e19fe869333135360197a7eb55994fee3e8d9680aedfdf7674f3ad7b84d59d7eab03579ffc10c7093093bc48ec84252aa1b30f40f5e838f1443e15e2772a39f4e774eb052097e8881e94f15457b779fa2af2bbc9a993687657c7704ac8a37c25c1df4289eb4c70da45f2fd46bc0f78259767d3dd478a7c369cf866758bc36d9bd8e2e3c9fb0cf7fd6073ebf630c1f67fa7d303c07da40b36749d157ea37965fef810f2ea05ae6fc7d96a8f3470d73e15b22b42e8d6986dbfe5303256b2b3560372c4452ffb2a04fb7c6691489f70cb46831be0679117f7"+    (mconcat $+      [ "92263cbf6ac376499f68a4289d3bb59e5a22335eba63a32e6410249155b956b6a3"+      , "b48d4a44906b18b897127300b375b8f834f1ceffc70880a885f47c33876717e392"+      , "be57f7da3ae58da4fd1f43daa7e44bb82d3717af4319349c24cd31e46d295856b0"+      , "441b6b289992a11ced1cc3bf3011604590244a3eb737ff221129215e4e4347f491"+      , "5d41292b5173d196eb9add693be5319fdadc242906178bb6c0286c9b6ca6012746"+      , "711f58c8c392016b2fdfc09c64f0f6b6ab7b"+      ]+    )+    "3b840e20e9555e9fb031c4ba1f1747ce25cc1d0ff664be676b9b4a90641ff194"+    Argon2i_1_3+    (Params 3 7994791)++-- unit_pwhash_argon2i_4 is skipped because it is a test for an incorrect output+-- size, which is impossible due to stronger types in this library++unit_pwhash_argon2i_5 :: Assertion+unit_pwhash_argon2i_5 =+  pwhash_test_vector+    @190+    "c121209f0ba70aed93d49200e5dc82cce013cef25ea31e160bf8db3cf448a59d1a56f6c19259e18ea020553cb75781761d112b2d949a297584c65e60df95ad89c4109825a3171dc6f20b1fd6b0cdfd194861bc2b414295bee5c6c52619e544abce7d520659c3d51de2c60e89948d830695ab38dcb75dd7ab06a4770dd4bc7c8f335519e04b038416b1a7dbd25c026786a8105c5ffe7a0931364f0376ae5772be39b51d91d3281464e0f3a128e7155a68e87cf79626ffca0b2a3022fc8420"+    (mconcat $+      [ "4a857e2ee8aa9b6056f2424e84d24a72473378906ee04a46cb05311502d5250b82"+      , "ad86b83c8f20a23dbb74f6da60b0b6ecffd67134d45946ac8ebfb3064294bc097d"+      , "43ced68642bfb8bbbdd0f50b30118f5e"+      ]+    )+    "39d82eef32010b8b79cc5ba88ed539fbaba741100f2edbeca7cc171ffeabf258"+    Argon2i_1_3+    (Params 3 1432947)++unit_pwhash_argon2i_6 :: Assertion+unit_pwhash_argon2i_6 =+  pwhash_test_vector+    @178+    "91c337ce8918a5805a59b00bd1819d3eb4356807cbd2a80b271c4b482dce03f5b02ae4eb831ff668cbb327b93c300b41da4852e5547bea8342d518dd9311aaeb5f90eccf66d548f9275631f0b1fd4b299cec5d2e86a59e55dc7b3afab6204447b21d1ef1da824abaf31a25a0d6135c4fe81d34a06816c8a6eab19141f5687108500f3719a862af8c5fee36e130c69921e11ce83dfc72c5ec3b862c1bccc5fd63ad57f432fbcca6f9e18d5a59015950cdf053"+    (mconcat $+      [ "c7b09aec680e7b42fedd7fc792e78b2f6c1bea8f4a884320b648f81e8cf515e8ba"+      , "9dcfb11d43c4aae114c1734aa69ca82d44998365db9c93744fa28b63fd16000e82"+      , "61cbbe083e7e2da1e5f696bde0834fe53146d7e0e35e7de9920d041f5a5621aabe"+      , "02da3e2b09b405b77937efef3197bd5772e41fdb73fb5294478e45208063b5f58e"+      , "089dbeb6d6342a909c1307b3fff5fe2cf4da56bdae50848f"+      ]+    )+    "039c056d933b475032777edbaffac50f143f64c123329ed9cf59e3b65d3f43b6"+    Argon2i_1_3+    (Params 3 4886999)++-- unit_pwhash_argon2i_7 is skipped because it is a test for an incorrect+-- opslimit.+-- XXX: Maybe we could encode this restriction in types?++unit_pwhash_argon2i_8 :: Assertion+unit_pwhash_argon2i_8 =+  pwhash_test_vector+    @167+    "e942951dfbc2d508294b10f9e97b47d0cd04e668a043cb95679cc1139df7c27cd54367688725be9d069f5704c12223e7e4ca181fbd0bed18bb4634795e545a6c04a7306933a41a794baedbb628d41bc285e0b9084055ae136f6b63624c874f5a1e1d8be7b0b7227a171d2d7ed578d88bfdcf18323198962d0dcad4126fd3f21adeb1e11d66252ea0c58c91696e91031bfdcc2a9dc0e028d17b9705ba2d7bcdcd1e3ba75b4b1fea"+    (mconcat $+      [ "a14975c26c088755a8b715ff2528d647cd343987fcf4aa25e7194a8417fb2b4b3f"+      , "7268da9f3182b4cfb22d138b2749d673a47ecc7525dd15a0a3c66046971784bb63"+      , "d7eae24cc84f2631712075a10e10a96b0e0ee67c43e01c423cb9c44e5371017e9c"+      , "496956b632158da3fe12addecb88912e6759bc37f9af2f45af72c5cae3b179ffb6"+      , "76a697de6ebe45cd4c16d4a9d642d29ddc0186a0a48cb6cd62bfc3dd229d313b30"+      , "1560971e740e2cf1f99a9a090a5b283f35475057e96d7064e2e0fc81984591068d"+      , "55a3b4169f22cccb0745a2689407ea1901a0a766eb99"+      ]+    )+    "3d968b2752b8838431165059319f3ff8910b7b8ecb54ea01d3f54769e9d98daf"+    Argon2i_1_3+    (Params 3 1784128)
test/Test/Crypto/Random.hs view
@@ -2,8 +2,6 @@ -- -- SPDX-License-Identifier: MPL-2.0 -{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}- module Test.Crypto.Random where  import Test.HUnit ((@?=), Assertion)@@ -16,7 +14,7 @@  import Crypto.Random (generate) -import qualified Crypto.Secretbox as Secretbox+import qualified Crypto.Encrypt.Secretbox as Secretbox   -- Well, this is kinda stupid, because we merely generate one random sequence,
+ test/Test/Crypto/Secretbox.hs view
@@ -0,0 +1,32 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | “Integration” tests: using Secretbox with our helpers.+module Test.Crypto.Secretbox where++import Hedgehog (Property, forAll, property, tripping)+import Hedgehog.Internal.Property (forAllT)++import Control.Monad.IO.Class (liftIO)+import Data.ByteString (ByteString)++import qualified Hedgehog.Gen as G+import qualified Hedgehog.Range as R++import qualified Crypto.Key as Key (generate)+import qualified Crypto.Random (generate)++import qualified Crypto.Encrypt.Secretbox as Secretbox+++hprop_encode_decode :: Property+hprop_encode_decode = property $ do+    key <- forAllT $ liftIO Key.generate+    nonce <- forAllT $ liftIO $ Crypto.Random.generate @ByteString+    msg <- forAll $ G.bytes (R.linear 0 1_000)+    tripping msg (encodeBs key nonce) (decodeBs key nonce)+  where+    -- We need to specify the type of the cyphertext as it is polymorphic+    encodeBs key nonce msg = Secretbox.create key nonce msg :: ByteString+    decodeBs key nonce ct = Secretbox.open key nonce ct :: Maybe ByteString