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 +1/−0
- crypto-sodium.cabal +31/−28
- lib/Crypto/Encrypt/Box.hs +84/−0
- lib/Crypto/Encrypt/Secretbox.hs +70/−0
- lib/Crypto/Init.hs +17/−0
- lib/Crypto/Key.hs +198/−0
- lib/Crypto/Key/Internal.hs +106/−0
- lib/Crypto/Pwhash/Internal.hs +82/−0
- lib/Crypto/Random.hs +3/−0
- test/Test/Crypto/Gen.hs +36/−0
- test/Test/Crypto/Key/Derivation.hs +49/−0
- test/Test/Crypto/Pwhash.hs +277/−0
- test/Test/Crypto/Random.hs +1/−3
- test/Test/Crypto/Secretbox.hs +32/−0
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