packages feed

crypto-sodium 0.0.3.1 → 0.0.4.0

raw patch · 22 files changed

+825/−234 lines, 22 filesdep +base16dep +deepseqdep +randomdep −base16-bytestringdep ~NaClPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: base16, deepseq, random

Dependencies removed: base16-bytestring

Dependency ranges changed: NaCl

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.Internal: [nonce] :: DerivationSlipData -> !Salt ByteString
+ Crypto.Encrypt.Public: decrypt :: (ByteArrayAccess skBytes, ByteArrayAccess pkBytes, ByteArrayAccess nonceBytes, ByteArray ptBytes, ByteArrayAccess ctBytes) => SecretKey skBytes -> PublicKey pkBytes -> Nonce nonceBytes -> ctBytes -> Maybe ptBytes
+ Crypto.Encrypt.Public: encrypt :: (ByteArrayAccess pkBytes, ByteArrayAccess skBytes, ByteArrayAccess nonceBytes, ByteArrayAccess ptBytes, ByteArray ctBytes) => PublicKey pkBytes -> SecretKey skBytes -> Nonce nonceBytes -> ptBytes -> ctBytes
+ Crypto.Encrypt.Public: keypair :: IO (PublicKey ByteString, SecretKey ScrubbedBytes)
+ Crypto.Encrypt.Public: keypairFromSeed :: ByteArrayAccess seed => Seed seed -> IO (PublicKey ByteString, SecretKey ScrubbedBytes)
+ Crypto.Encrypt.Public: toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)
+ Crypto.Encrypt.Public: toPublicKey :: ByteArrayAccess bytes => bytes -> Maybe (PublicKey bytes)
+ Crypto.Encrypt.Public: toSecretKey :: ByteArrayAccess bytes => bytes -> Maybe (SecretKey bytes)
+ Crypto.Encrypt.Public: type Nonce a = SizedByteArray CRYPTO_BOX_NONCEBYTES a
+ Crypto.Encrypt.Public: type PublicKey a = SizedByteArray CRYPTO_BOX_PUBLICKEYBYTES a
+ Crypto.Encrypt.Public: type SecretKey a = SizedByteArray CRYPTO_BOX_SECRETKEYBYTES a
+ Crypto.Encrypt.Public: unsafeKeypairFromSeed :: ByteArrayAccess seed => Seed seed -> (PublicKey ByteString, SecretKey ScrubbedBytes)
+ Crypto.Encrypt.Symmetric: decrypt :: (ByteArrayAccess keyBytes, ByteArrayAccess nonceBytes, ByteArray ptBytes, ByteArrayAccess ctBytes) => Key keyBytes -> Nonce nonceBytes -> ctBytes -> Maybe ptBytes
+ Crypto.Encrypt.Symmetric: encrypt :: (ByteArrayAccess keyBytes, ByteArrayAccess nonceBytes, ByteArrayAccess ptBytes, ByteArray ctBytes) => Key keyBytes -> Nonce nonceBytes -> ptBytes -> ctBytes
+ Crypto.Encrypt.Symmetric: toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)
+ Crypto.Encrypt.Symmetric: toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)
+ Crypto.Encrypt.Symmetric: type Key a = SizedByteArray CRYPTO_SECRETBOX_KEYBYTES a
+ Crypto.Encrypt.Symmetric: type Nonce a = SizedByteArray CRYPTO_SECRETBOX_NONCEBYTES a
+ Crypto.Encrypt.Symmetric.Stream: toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)
+ Crypto.Encrypt.Symmetric.Stream: type Key a = SizedByteArray CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES a
+ Crypto.Internal.Random: generateInsecure :: forall n. KnownNat n => IO (SizedByteArray n ByteString)
+ Crypto.Internal.Verify: verifyBytes32 :: (ByteArrayAccess ba1, ByteArrayAccess ba2) => SizedByteArray 32 ba1 -> SizedByteArray 32 ba2 -> IO Bool
+ Crypto.Key.Internal: [salt] :: DerivationSlipData -> !Salt ByteString
+ Crypto.Mac: create :: (ByteArray authBytes, ByteArrayAccess keyBytes, ByteArrayAccess msg) => Key keyBytes -> msg -> Authenticator authBytes
+ Crypto.Mac: toAuthenticator :: ByteArrayAccess ba => ba -> Maybe (Authenticator ba)
+ Crypto.Mac: toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)
+ Crypto.Mac: type Authenticator a = SizedByteArray CRYPTO_AUTH_BYTES a
+ Crypto.Mac: type Key a = SizedByteArray CRYPTO_AUTH_KEYBYTES a
+ Crypto.Mac: verify :: (ByteArrayAccess authBytes, ByteArrayAccess msg, ByteArrayAccess keyBytes) => Key keyBytes -> msg -> Authenticator authBytes -> Bool
+ Crypto.Nonce: generate :: KnownNat n => IO (SizedByteArray n ByteString)
+ Crypto.Sign: create :: (ByteArrayAccess skBytes, ByteArrayAccess ptBytes, ByteArray ctBytes) => SecretKey skBytes -> ptBytes -> ctBytes
+ Crypto.Sign: keypair :: IO (PublicKey ByteString, SecretKey ScrubbedBytes)
+ Crypto.Sign: keypairFromSeed :: ByteArrayAccess seed => Seed seed -> IO (PublicKey ByteString, SecretKey ScrubbedBytes)
+ Crypto.Sign: open :: (ByteArrayAccess pkBytes, ByteArray ptBytes, ByteArrayAccess ctBytes) => PublicKey pkBytes -> ctBytes -> Maybe ptBytes
+ Crypto.Sign: toPublicKey :: ByteArrayAccess bytes => bytes -> Maybe (PublicKey bytes)
+ Crypto.Sign: toSecretKey :: ByteArrayAccess bytes => bytes -> Maybe (SecretKey bytes)
+ Crypto.Sign: type PublicKey a = SizedByteArray CRYPTO_SIGN_PUBLICKEYBYTES a
+ Crypto.Sign: type SecretKey a = SizedByteArray CRYPTO_SIGN_SECRETKEYBYTES a
+ Crypto.Sign: unsafeKeypairFromSeed :: ByteArrayAccess seed => Seed seed -> (PublicKey ByteString, SecretKey ScrubbedBytes)

Files

CHANGELOG.md view
@@ -7,3 +7,7 @@ * `sodiumInit` * Random bytes generation: `Crypto.Random` * Key derivation: `Crypto.Key.derive` and `Crypto.Key.rederive`+* MAC: `Crypto.Mac`, `Crypto.Mac.Lazy`+* Random nonce generation: `Crypto.Nonce`+* Public-key signatures: `Crypto.Sign`+* Keypair generation from seed: `Crypto.Sign` and `Crypto.Encrypt.Public`
crypto-sodium.cabal view
@@ -1,13 +1,13 @@ cabal-version: 1.18 --- This file has been generated from package.yaml by hpack version 0.33.0.+-- This file has been generated from package.yaml by hpack version 0.34.4. -- -- see: https://github.com/sol/hpack ----- hash: 8f2d927fece7ed762df312f2f4393f5e471fe0068b66c7aa76c07a7ca34e220f+-- hash: 25a103374b2b14f150bf4f1ccb243ed60f601e794e2345bee0adec07b08f3a47  name:           crypto-sodium-version:        0.0.3.1+version:        0.0.4.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@@ -41,11 +41,13 @@                 .                 == Secret-key cryptography                 .-                * Authenticated symmetric-key encryption: "Crypto.Encrypt.Secretbox"+                * Authenticated symmetric-key encryption: "Crypto.Encrypt.Symmetric"+                * Message authentication codes: "Crypto.Mac"                 .                 == Public-key cryptography                 .-                * Authenticated public-key encryption: "Crypto.Encrypt.Box"+                * Authenticated public-key encryption: "Crypto.Encrypt.Public"+                * Public-key signatures: "Crypto.Sign"                 .                 == Additional primitives                 .@@ -55,8 +57,8 @@ homepage:       https://github.com/serokell/haskell-crypto#readme bug-reports:    https://github.com/serokell/haskell-crypto/issues author:         Kirill Elagin <kirelagin@serokell.io>-maintainer:     Kirill Elagin <kirelagin@serokell.io>-copyright:      2020 Serokell+maintainer:     Serokell <libraries@serokell.io>+copyright:      2021 Serokell license:        MPL-2.0 license-file:   LICENSES/MPL-2.0.txt build-type:     Simple@@ -70,55 +72,92 @@  library   exposed-modules:-      Crypto.Encrypt.Box-      Crypto.Encrypt.Secretbox+      Crypto.Encrypt.Public+      Crypto.Encrypt.Symmetric+      Crypto.Encrypt.Symmetric.Stream       Crypto.Init+      Crypto.Internal.Random+      Crypto.Internal.Verify       Crypto.Key       Crypto.Key.Internal+      Crypto.Mac+      Crypto.Nonce       Crypto.Pwhash.Internal       Crypto.Random+      Crypto.Sign   other-modules:       Paths_crypto_sodium   hs-source-dirs:       lib-  default-extensions: DataKinds FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving KindSignatures LambdaCase MultiParamTypeClasses NamedFieldPuns NumericUnderscores OverloadedStrings PolyKinds ScopedTypeVariables TypeApplications+  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+      NaCl >=0.0.4.0 && <0.1     , base >=4.10 && <4.15     , bytestring >=0.9 && <0.11     , cereal >=0.1 && <0.6     , libsodium >=1.0.11 && <2     , memory >=0.14.15 && <0.16-    , safe-exceptions >=0.1 && <0.2+    , random >=1.0 && <1.3+    , safe-exceptions ==0.1.*   default-language: Haskell2010  test-suite test   type: exitcode-stdio-1.0   main-is: Test.hs   other-modules:+      Test.Crypto.Encrypt.Public+      Test.Crypto.Encrypt.Symmetric       Test.Crypto.Gen       Test.Crypto.Key.Derivation+      Test.Crypto.Nonce       Test.Crypto.Pwhash       Test.Crypto.Random-      Test.Crypto.Secretbox+      Test.Crypto.Sign       Paths_crypto_sodium   hs-source-dirs:       test-  default-extensions: DataKinds FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving KindSignatures LambdaCase MultiParamTypeClasses NamedFieldPuns NumericUnderscores OverloadedStrings PolyKinds ScopedTypeVariables TypeApplications+  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+    , base16 >=0.1.1 && <0.4     , bytestring >=0.9 && <0.11     , crypto-sodium+    , deepseq     , hedgehog     , libsodium >=1.0.11 && <2     , memory >=0.14.15 && <0.16-    , safe-exceptions >=0.1 && <0.2+    , safe-exceptions ==0.1.*     , tasty     , tasty-hedgehog     , tasty-hunit
− lib/Crypto/Encrypt/Box.hs
@@ -1,86 +0,0 @@--- SPDX-FileCopyrightText: 2020 Serokell------ SPDX-License-Identifier: MPL-2.0--{-# OPTIONS_HADDOCK not-home #-}---- ! 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/Public.hs view
@@ -0,0 +1,154 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++{-# OPTIONS_HADDOCK not-home #-}++-- ! 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.Public as Public+--+-- encrypted = Public.'encrypt' pk sk nonce message+-- decrypted = Public.'decrypt' 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.Public+  (+  -- * Keys+    PublicKey+  , toPublicKey+  , SecretKey+  , toSecretKey+  , keypair+  , keypairFromSeed+  , unsafeKeypairFromSeed++  -- * Nonce+  , Nonce+  , toNonce++  -- * Encryption/decryption+  , encrypt+  , decrypt+  ) where++import Data.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes, withByteArray)+import Data.ByteArray.Sized as Sized (SizedByteArray, alloc, allocRet)+import Data.ByteString (ByteString)+import Data.Functor (void)+import Data.Proxy (Proxy(..))+import System.IO.Unsafe (unsafePerformIO)++import qualified Libsodium as Na++import NaCl.Box+  (Nonce, PublicKey, SecretKey, keypair, toNonce, toPublicKey, toSecretKey)+import qualified NaCl.Box as NaCl.Box+++-- | Encrypt a message.+--+-- @+-- encrypted = Public.encrypt pk sk nonce message+-- @+--+-- *   @pk@ is the receiver’s public key, used for encryption.+--     @sk@ is the sender’s secret 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,+-- 'decrypt' will refuse to decrypt it.+encrypt+  ::  ( ByteArrayAccess pkBytes, ByteArrayAccess skBytes+      , ByteArrayAccess nonceBytes+      , ByteArrayAccess ptBytes, ByteArray ctBytes+      )+  => PublicKey pkBytes  -- ^ Receiver’s public key+  -> SecretKey skBytes  -- ^ Sender’s secret key+  -> Nonce nonceBytes  -- ^ Nonce+  -> ptBytes -- ^ Plaintext message+  -> ctBytes+encrypt = NaCl.Box.create+++-- | Decrypt a message.+--+-- @+-- decrypted = Public.decrypt sk pk nonce encrypted+-- @+--+-- * @sk@ is the receiver’s secret key, used for decription.+-- * @pk@ is the sender’s public key, used for authentication.+-- * @nonce@ is the same that was used for encryption.+-- * @encrypted@ is the output of 'encrypt'.+--+-- This function will return @Nothing@ if the encrypted message was tampered+-- with after it was encrypted.+decrypt+  ::  ( ByteArrayAccess skBytes, ByteArrayAccess pkBytes+      , ByteArrayAccess nonceBytes+      , ByteArray ptBytes, ByteArrayAccess ctBytes+      )+  => SecretKey skBytes  -- ^ Receiver’s secret key+  -> PublicKey pkBytes  -- ^ Sender’s public key+  -> Nonce nonceBytes  -- ^ Nonce+  -> ctBytes -- ^ Encrypted message (cyphertext)+  -> Maybe ptBytes+decrypt = NaCl.Box.open+++-- | Seed for deterministically generating a keypair.+--+-- In accordance with Libsodium's documentation, the seed must be of size+-- @Na.CRYPTO_BOX_SEEDBYTES@.+--+-- This type is parametrised by the actual data type that contains+-- bytes. This can be, for example, a @ByteString@.+type Seed a = SizedByteArray Na.CRYPTO_BOX_SEEDBYTES a+++-- | Generate a new 'SecretKey' together with its 'PublicKey' from a given seed.+keypairFromSeed+  :: ByteArrayAccess seed+  => Seed seed+  -> IO (PublicKey ByteString, SecretKey ScrubbedBytes)+keypairFromSeed seed = do+  allocRet Proxy $ \skPtr ->+    alloc $ \pkPtr ->+    withByteArray seed $ \sdPtr ->+    -- always returns 0, so we don’t check it+    void $ Na.crypto_box_seed_keypair pkPtr skPtr sdPtr++-- | Generate a new 'SecretKey' together with its 'PublicKey' from a given seed,+-- in a pure context.+unsafeKeypairFromSeed+  :: ByteArrayAccess seed+  => Seed seed+  -> (PublicKey ByteString, SecretKey ScrubbedBytes)+unsafeKeypairFromSeed = unsafePerformIO . keypairFromSeed
− lib/Crypto/Encrypt/Secretbox.hs
@@ -1,72 +0,0 @@--- SPDX-FileCopyrightText: 2020 Serokell------ SPDX-License-Identifier: MPL-2.0--{-# OPTIONS_HADDOCK not-home #-}---- ! 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/Encrypt/Symmetric.hs view
@@ -0,0 +1,94 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++{-# OPTIONS_HADDOCK not-home #-}++-- ! 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.Symmetric as Symmetric+--+-- encrypted = Symmetric.'encrypt' key nonce message+-- decrypted = Symmetric.'decrypt' key nonce encrypted+-- @+--+-- In NaCl this is know as a “Secretbox”. 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 using 'encrypt', store it somewhere+-- (it is just a sequence of bytes), and when you need it in the+-- future, you “open” it with 'decrypt' using the same secret key.+module Crypto.Encrypt.Symmetric+  (+  -- * Keys+    Key+  , toKey++  -- * Nonce+  , Nonce+  , toNonce++  -- * Encryption/decryption+  , encrypt+  , decrypt+  ) where++import NaCl.Secretbox (Key, Nonce, toKey, toNonce)+import Data.ByteArray (ByteArray, ByteArrayAccess)++import qualified NaCl.Secretbox as NaCl.Secretbox+++-- | Encrypt a message.+--+-- @+-- encrypted = Symmetric.encrypt 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 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.+encrypt+  ::  ( ByteArrayAccess keyBytes, ByteArrayAccess nonceBytes+      , ByteArrayAccess ptBytes, ByteArray ctBytes+      )+  => Key keyBytes  -- ^ Secret key+  -> Nonce nonceBytes  -- ^ Nonce+  -> ptBytes -- ^ Plaintext message+  -> ctBytes+encrypt = NaCl.Secretbox.create+++-- | Decrypt a message.+--+-- @+-- decrypted = Symmetric.decrypt key nonce encrypted+-- @+--+-- * @key@ and @nonce@ are the same that were used for encryption.+-- * @encrypted@ is the output of 'create'.+--+-- This function will return @Nothing@ if the encrypted message was tampered+-- with after it was encrypted.+decrypt+  ::  ( ByteArrayAccess keyBytes, ByteArrayAccess nonceBytes+      , ByteArray ptBytes, ByteArrayAccess ctBytes+      )+  => Key keyBytes  -- ^ Secret key+  -> Nonce nonceBytes  -- ^ Nonce+  -> ctBytes -- ^ Encrypted message (cyphertext)+  -> Maybe ptBytes+decrypt = NaCl.Secretbox.open
+ lib/Crypto/Encrypt/Symmetric/Stream.hs view
@@ -0,0 +1,41 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++{-# OPTIONS_HADDOCK not-home #-}++-- | Symmetric authenticated encryption for streams.+--+-- This module provides generic types for Sodium-based streaming+-- encryption. It does not provide any functionality itself.+--+-- There are separate packages that actually implement this functionality+-- for specific streaming libraries:+--+-- * @crypto-sodium-streamly@ for @streamly@ streams.+module Crypto.Encrypt.Symmetric.Stream+  (+  -- * Keys+    Key+  , toKey+  ) where++import Data.ByteArray (ByteArrayAccess)+import Data.ByteArray.Sized (SizedByteArray, sizedByteArray)++import qualified Libsodium as Na+++-- | Encryption key that can be used for streaming symmetric encryption.+--+-- This type is parametrised by the actual data type that contains+-- bytes. This can be, for example, a @ByteString@, but, since this+-- is a secret key, it is better to use @ScrubbedBytes@.+type Key a = SizedByteArray Na.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES a++-- | Make a 'Key' from an arbitrary byte array.+--+-- This function returns @Just@ if and only if the byte array has+-- the right length to be used as a key with a streaming symmetric encryption.+toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)+toKey = sizedByteArray
+ lib/Crypto/Internal/Random.hs view
@@ -0,0 +1,31 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | Generate simple insecure random data.+module Crypto.Internal.Random+  ( generateInsecure+  ) where++import Data.ByteArray.Sized (SizedByteArray)+import Data.ByteString (ByteString)+import GHC.TypeLits (KnownNat)++import Crypto.Random (generate)+++-- | Generate a sequence of random bytes.+--+-- The output of this function is NOT suitable for secret keys.+generateInsecure+  :: forall n. (KnownNat n)+  => IO (SizedByteArray n ByteString)+generateInsecure+{-+  = unsafeSizedByteArray . BS.pack . take len . randoms <$> newStdGen+  where+    len = fromIntegral $ natVal (Proxy :: Proxy n)+-}+  -- Haddock above is actually a lie. We use the same random generator+  -- as for keys, because, after benchmarking, it happens to be faster :/.+  = generate
+ lib/Crypto/Internal/Verify.hs view
@@ -0,0 +1,35 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | @crypto_verify_*@+module Crypto.Internal.Verify+  ( verifyBytes32+  ) where++import Data.ByteArray (ByteArrayAccess, withByteArray)+import Data.ByteArray.Sized (SizedByteArray)++import qualified Libsodium as Na+++-- | Compare two byte arrays of length 32.+verifyBytes32+  :: ( ByteArrayAccess ba1+     , ByteArrayAccess ba2+     )+  => SizedByteArray 32 ba1  -- ^ First byte array+  -> SizedByteArray 32 ba2  -- ^ Second byte array+  -> IO Bool+verifyBytes32 bytes1 bytes2 =+  withByteArray bytes1 $ \ptr1 ->+  withByteArray bytes2 $ \ptr2 -> do+    -- TODO: I have no idea what I am doing+    --+    -- - Sodium also checks that pointers are different?+    -- - This kind of double comparison was added in+    --   c5a9d46386f917aa0ff1bfb711450f9af1d79a17+    --   (why?)+    res1 <- Na.crypto_verify_32 ptr1 ptr2+    res2 <- Na.sodium_memcmp ptr2 ptr1 32+    pure $ res1 == 0 && res2 == 0
lib/Crypto/Key.hs view
@@ -94,7 +94,7 @@ import Data.ByteArray.Sized (ByteArrayN, SizedByteArray) import Data.Kind (Constraint) import GHC.TypeLits (type (<=), KnownNat)-import System.IO.Unsafe (unsafeDupablePerformIO)+import System.IO.Unsafe (unsafePerformIO)  import qualified Libsodium as Na @@ -183,7 +183,7 @@   -> passwd  -- ^ Password to rederive from.   -> Maybe key rederive slip passwd =-  unsafeDupablePerformIO $ I.rederive slip passwd+  unsafePerformIO $ I.rederive slip passwd   -- This IO is safe, because it is pure.  
lib/Crypto/Key/Internal.hs view
@@ -28,11 +28,11 @@  import qualified Libsodium as Na +import Crypto.Nonce (generate) import Crypto.Pwhash.Internal (Algorithm (Argon2id_1_3), Params (..), Salt, pwhash)-import Crypto.Random (generate)  --- | Opaque bytes that contain the nonce and pwhash params.+-- | Opaque bytes that contain the salt and pwhash params. type DerivationSlip = ByteString  -- | Data contained in a derivation slip.@@ -45,23 +45,23 @@ -- used (for forward-compatibility). data DerivationSlipData = DerivationSlipData   { params :: !Params-  , nonce :: !(Salt ByteString)+  , salt :: !(Salt ByteString)   }   deriving (Eq, Show)  instance Serialize DerivationSlipData where-  put (DerivationSlipData Params{opsLimit, memLimit} nonce) = do+  put (DerivationSlipData Params{opsLimit, memLimit} salt) = do     put (1 :: Word8)  -- algorithm marker for forward-compatibility     put opsLimit >> put memLimit-    put (unSizedByteArray nonce)+    put (unSizedByteArray salt)   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+    msalt <- sizedByteArray <$> get @ByteString+    case msalt of+      Nothing -> fail "Unexpected salt size"+      Just salt -> pure $ DerivationSlipData params salt   -- | Encode derivation slip data into bytes.@@ -85,9 +85,9 @@   -> passwd   -> IO (Maybe (key, DerivationSlip)) derive params passwd = do-  nonce <- generate-  mkey <- pwhash Argon2id_1_3 params passwd nonce-  let slip = DerivationSlipData params nonce+  salt <- generate+  mkey <- pwhash Argon2id_1_3 params passwd salt+  let slip = DerivationSlipData params salt   pure $ fmap (, derivationSlipEncode slip) mkey  -- | Derive the same key form the same password again.@@ -102,5 +102,5 @@ rederive slip passwd =   case derivationSlipDecode slip of     Nothing -> pure Nothing-    Just (DerivationSlipData{params, nonce}) ->-      pwhash Argon2id_1_3 params passwd nonce+    Just (DerivationSlipData{params, salt}) ->+      pwhash Argon2id_1_3 params passwd salt
+ lib/Crypto/Mac.hs view
@@ -0,0 +1,69 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++{-# OPTIONS_HADDOCK not-home #-}++-- ! 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.++-- | Message authentication codes.+--+-- It is best to import this module qualified:+--+-- @+-- import qualified Crypto.Mac as Mac+--+-- authenticator = Mac.'create' key message+-- if Mac.'verify' key message authenticator+-- then {- Ok! -}+-- else {- Fail! -}+-- @+--+-- A message authenticator is like a signature, except that the key is+-- secret. It can be used when it is not necessary to encrypt the data,+-- but its integrity needs to be guaranteed.+module Crypto.Mac+  (+  -- * Keys+    Key+  , toKey++  -- * Authenticator tags+  , Authenticator+  , toAuthenticator++  -- * Authentication+  , create+  , verify+  ) where++import NaCl.Auth (Authenticator, Key, toAuthenticator, toKey, verify)+import Data.ByteArray (ByteArray, ByteArrayAccess)++import qualified NaCl.Auth as NaCl.Auth+++-- | Create an authenticator for a message.+--+-- @+-- authenticator = Mac.create key message+-- @+--+-- *   @key@ is the secret key used for authentication. See "Crypto.Key" for how+--     to get one.+--+-- *   @message@ is the data you are authenticating.+--+-- This function produces authentication data, so if anyone modifies the message,+-- @verify@ will return @False@.+create+  ::  ( ByteArray authBytes+      , ByteArrayAccess keyBytes+      , ByteArrayAccess msg+      )+  => Key keyBytes  -- ^ Secret key.+  -> msg  -- ^ Message to authenticate.+  -> Authenticator authBytes+create = NaCl.Auth.create
+ lib/Crypto/Nonce.hs view
@@ -0,0 +1,46 @@+{-# 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 nonces.+--+-- A “nonce” is additional input provided to an encryption algorithm.+-- The most important rule is that you cannot use the same nonce to encrypt+-- more than one message. What will happen if you reuse a nonce depends+-- on the details of the cryptographic algorithm, but, in general, expect+-- the cryptography to fail completely in this case.+--+-- = Random nonce generation+--+-- The easiest way to guarantee that nonces do not repeat is to use a+-- random nonce every time. If the nonce is large enough, then the+-- probably of a random nonce repeating will be negligible.+-- All cryptographic functions provided by this library can be+-- safely used with random nonces.+--+-- The 'generate' function is great at generating new random nonces.+-- The nonces are produced using the standard @random@ package, so+-- the generation is very fast.+module Crypto.Nonce+  (+  -- * Random nonce generation+    generate+  ) where++import Data.ByteArray.Sized (SizedByteArray)+import Data.ByteString (ByteString)+import GHC.TypeLits (KnownNat)++import Crypto.Internal.Random (generateInsecure)+++-- | Generate a new random nonce.+--+-- Note: random data generated by the functions in this module is only suitable+-- to be used as a nonce, but never as a secret key.+generate :: KnownNat n => IO (SizedByteArray n ByteString)+generate = generateInsecure
lib/Crypto/Random.hs view
@@ -15,7 +15,7 @@ import qualified Libsodium as Na  --- | Generate a sequence of cryptographically-secure renadom bytes.+-- | Generate a sequence of cryptographically-secure random bytes. -- -- The output of this function is suitable to generate secret keys. --
+ lib/Crypto/Sign.hs view
@@ -0,0 +1,87 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++{-# OPTIONS_HADDOCK not-home #-}++-- ! 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 signatures.+--+-- It is best to import this module qualified:+--+-- @+-- import qualified Crypto.Sign as Sign+--+-- signed = Sign.'create' sk message+-- verified = Sign.'open' pk signed+-- @+--+-- Functions in this modules work with /combined/ signatures.+-- This means that when you sign a message, it will be copied as is+-- and then a signature will be prepended. So you should treat the+-- resulting value as a transparent (because it is not encrypted)+-- package with a signature attached on top.+--+-- Instead of accessing the message directly, you should use+-- 'open', which will verify the signature and return a copy of the+-- original message only if the signature was valid.+module Crypto.Sign+  (+  -- * Keys+    PublicKey+  , toPublicKey+  , SecretKey+  , toSecretKey+  , keypair+  , keypairFromSeed+  , unsafeKeypairFromSeed++  -- * Signing/verifying+  , create+  , open+  ) where++import Data.ByteArray (ByteArrayAccess, ScrubbedBytes, withByteArray)+import Data.ByteString (ByteString)+import Data.ByteArray.Sized (SizedByteArray, alloc, allocRet)+import Data.Functor (void)+import Data.Proxy (Proxy(..))+import System.IO.Unsafe (unsafePerformIO)++import qualified Libsodium as Na++import NaCl.Sign+  (PublicKey, SecretKey, create, keypair, open, toPublicKey, toSecretKey)++-- | Seed for deterministically generating a keypair.+--+-- In accordance with Libsodium's documentation, the seed must be of size+-- @Na.CRYPTO_SIGN_SEEDBYTES@.+--+-- This type is parametrised by the actual data type that contains+-- bytes. This can be, for example, a @ByteString@.+type Seed a = SizedByteArray Na.CRYPTO_SIGN_SEEDBYTES a+++-- | Generate a new 'SecretKey' together with its 'PublicKey' from a given seed.+keypairFromSeed+  :: ByteArrayAccess seed+  => Seed seed+  -> IO (PublicKey ByteString, SecretKey ScrubbedBytes)+keypairFromSeed seed = do+  allocRet Proxy $ \skPtr ->+    alloc $ \pkPtr ->+    withByteArray seed $ \sdPtr ->+    -- always returns 0, so we don’t check it+    void $ Na.crypto_sign_seed_keypair pkPtr skPtr sdPtr++-- | Generate a new 'SecretKey' together with its 'PublicKey' from a given seed,+-- in a pure context.+unsafeKeypairFromSeed+  :: ByteArrayAccess seed+  => Seed seed+  -> (PublicKey ByteString, SecretKey ScrubbedBytes)+unsafeKeypairFromSeed = unsafePerformIO . keypairFromSeed
+ test/Test/Crypto/Encrypt/Public.hs view
@@ -0,0 +1,43 @@+-- SPDX-FileCopyrightText: 2021 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | “Integration” tests: using Public with our helpers.+module Test.Crypto.Encrypt.Public where++import Hedgehog (Property, evalMaybe, forAll, property, tripping)+import Hedgehog.Internal.Property (forAllT)++import Control.Monad.IO.Class (liftIO)+import Data.ByteArray.Sized (sizedByteArray)+import Data.ByteString (ByteString)++import qualified Libsodium as Na++import qualified Hedgehog.Gen as G+import qualified Hedgehog.Range as R++import qualified Crypto.Encrypt.Public as Public+++nonceSize :: R.Range Int+nonceSize = R.singleton $ fromIntegral Na.crypto_box_noncebytes++seedSize :: R.Range Int+seedSize = R.singleton $ fromIntegral Na.crypto_box_seedbytes+++hprop_encode_decode_seed :: Property+hprop_encode_decode_seed = property $ do+    seed1 <- evalMaybe . sizedByteArray =<< forAll (G.bytes seedSize)+    seed2 <- evalMaybe . sizedByteArray =<< forAll (G.bytes seedSize)+    (pkS, skS) <- forAllT . liftIO $ Public.keypairFromSeed seed1+    (pkR, skR) <- forAllT . liftIO $ Public.keypairFromSeed seed2+    nonceBytes <- forAll $ G.bytes nonceSize+    let Just nonce = Public.toNonce nonceBytes+    msg <- forAll $ G.bytes (R.linear 0 1_000)+    tripping msg (encodeBs pkR skS nonce) (decodeBs skR pkS nonce)+  where+    -- We need to specify the type of the cyphertext as it is polymorphic+    encodeBs pkR skS nonce msg = Public.encrypt pkR skS nonce msg :: ByteString+    decodeBs skR pkS nonce ct = Public.decrypt skR pkS nonce ct :: Maybe ByteString
+ test/Test/Crypto/Encrypt/Symmetric.hs view
@@ -0,0 +1,32 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | “Integration” tests: using Symmetric with our helpers.+module Test.Crypto.Encrypt.Symmetric 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.Symmetric as Symmetric+++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 = Symmetric.encrypt key nonce msg :: ByteString+    decodeBs key nonce ct = Symmetric.decrypt key nonce ct :: Maybe ByteString
+ test/Test/Crypto/Nonce.hs view
@@ -0,0 +1,69 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++module Test.Crypto.Nonce where++import Test.HUnit ((@?), (@?=), Assertion)++import Control.DeepSeq (deepseq)+import Data.ByteArray.Sized (unSizedByteArray)+import Data.ByteString (ByteString)+import Data.Ratio ((%))+import System.CPUTime (getCPUTime)++import qualified Data.ByteString as BS+import qualified Libsodium as Na++import Crypto.Nonce (generate)++import qualified Crypto.Encrypt.Symmetric as Symmetric+import qualified Crypto.Nonce as Nonce (generate)+import qualified Crypto.Pwhash.Internal as Pwhash+import qualified Crypto.Random as Random (generate)+++-- Well, this is kinda stupid, because we merely generate one random sequence,+-- but this is just to check that the lengths are correctly propagated+-- through types. So, good enough.++unit_generate_Symmetric_nonce :: Assertion+unit_generate_Symmetric_nonce = do+  nonce <- generate :: IO (Symmetric.Nonce ByteString)+  let bs = unSizedByteArray nonce+  BS.length bs @?= fromIntegral Na.crypto_secretbox_noncebytes++unit_generate_Pwhash_salt :: Assertion+unit_generate_Pwhash_salt = do+  nonce <- generate :: IO (Pwhash.Salt ByteString)+  let bs = unSizedByteArray nonce+  BS.length bs @?= fromIntegral Na.crypto_pwhash_saltbytes++++-- Benchmark to make sure this all makes sense and insecure nonse generation+-- is actually faster than cryptographically-secure generation.+unit_bench_against_crypto :: Assertion+unit_bench_against_crypto = do+    tNonce <- measure $ (unSizedByteArray <$> Nonce.generate @64)+    tCrypto <- measure $ (unSizedByteArray <$> Random.generate @ByteString @64)+    let ratio = fromRational (tNonce % tCrypto) :: Double++    -- XXX: The benchmark is disabled, because we don’t yet have an+    -- implementation that would actually be faster :/.+    --        vvvvvvvv+    ratio < 1 || True @? "Crypto gen is " <> show ratio <> "x faster"+    --        ^^^^^^^^+  where+    measure act = do+        t1 <- getCPUTime+        go 1000+        t2 <- getCPUTime+        pure $ t2 - t1+      where+        go :: Int -> IO ()+        go n+          | n <= 0 = pure ()+          | otherwise = do+              res <- act+              res `deepseq` go (n - 1)
test/Test/Crypto/Pwhash.hs view
@@ -13,10 +13,11 @@  import Data.ByteArray.Sized (sizedByteArray) import Data.ByteString (ByteString)+import Data.ByteString.Base16 (decodeBase16)+import Data.Either (fromRight) 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)@@ -34,9 +35,9 @@   -> 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 hash' = fromRight (error "impossible") . decodeBase16 $ hash+  let passwd' = fromRight (error "impossible") . decodeBase16 $ passwd+  let salt' = fromRight (error "impossible") . decodeBase16 $ 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 :/
test/Test/Crypto/Random.hs view
@@ -14,7 +14,7 @@  import Crypto.Random (generate) -import qualified Crypto.Encrypt.Secretbox as Secretbox+import qualified Crypto.Encrypt.Symmetric as Symmetric   -- Well, this is kinda stupid, because we merely generate one random sequence,@@ -23,14 +23,14 @@ -- -- Also it is not thread-safe, since we don’t call @sodiumInit@... -unit_generate_Secretbox_key :: Assertion-unit_generate_Secretbox_key = do-  key <- generate :: IO (Secretbox.Key ByteString)+unit_generate_Symmetric_key :: Assertion+unit_generate_Symmetric_key = do+  key <- generate :: IO (Symmetric.Key ByteString)   let bs = unSizedByteArray key   BS.length bs @?= fromIntegral Na.crypto_secretbox_keybytes -unit_generate_Secretbox_nonce :: Assertion-unit_generate_Secretbox_nonce = do-  nonce <- generate :: IO (Secretbox.Nonce ByteString)+unit_generate_Symmetric_nonce :: Assertion+unit_generate_Symmetric_nonce = do+  nonce <- generate :: IO (Symmetric.Nonce ByteString)   let bs = unSizedByteArray nonce   BS.length bs @?= fromIntegral Na.crypto_secretbox_noncebytes
− test/Test/Crypto/Secretbox.hs
@@ -1,32 +0,0 @@--- 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
+ test/Test/Crypto/Sign.hs view
@@ -0,0 +1,36 @@+-- SPDX-FileCopyrightText: 2021 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | “Integration” tests: using Sign with our helpers.+module Test.Crypto.Sign where++import Hedgehog (Property, evalMaybe, forAll, property, tripping)+import Hedgehog.Internal.Property (forAllT)++import Control.Monad.IO.Class (liftIO)+import Data.ByteArray.Sized (sizedByteArray)+import Data.ByteString (ByteString)++import qualified Hedgehog.Gen as G+import qualified Hedgehog.Range as R++import qualified Libsodium as Na++import qualified Crypto.Sign as Sign+++seedSize :: R.Range Int+seedSize = R.singleton $ fromIntegral Na.crypto_sign_seedbytes+++hprop_encode_decode_seed :: Property+hprop_encode_decode_seed = property $ do+    seed <- evalMaybe . sizedByteArray =<< forAll (G.bytes seedSize)+    (pk, sk) <- forAllT . liftIO $ Sign.keypairFromSeed seed+    msg <- forAll $ G.bytes (R.linear 0 1_000)+    tripping msg (encodeBs sk) (decodeBs pk)+  where+    -- We need to specify the type of the signed msg as it is polymorphic+    encodeBs sk msg = Sign.create sk msg :: ByteString+    decodeBs pk ct = Sign.open pk ct :: Maybe ByteString