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 +4/−0
- crypto-sodium.cabal +55/−16
- lib/Crypto/Encrypt/Box.hs +0/−86
- lib/Crypto/Encrypt/Public.hs +154/−0
- lib/Crypto/Encrypt/Secretbox.hs +0/−72
- lib/Crypto/Encrypt/Symmetric.hs +94/−0
- lib/Crypto/Encrypt/Symmetric/Stream.hs +41/−0
- lib/Crypto/Internal/Random.hs +31/−0
- lib/Crypto/Internal/Verify.hs +35/−0
- lib/Crypto/Key.hs +2/−2
- lib/Crypto/Key/Internal.hs +14/−14
- lib/Crypto/Mac.hs +69/−0
- lib/Crypto/Nonce.hs +46/−0
- lib/Crypto/Random.hs +1/−1
- lib/Crypto/Sign.hs +87/−0
- test/Test/Crypto/Encrypt/Public.hs +43/−0
- test/Test/Crypto/Encrypt/Symmetric.hs +32/−0
- test/Test/Crypto/Nonce.hs +69/−0
- test/Test/Crypto/Pwhash.hs +5/−4
- test/Test/Crypto/Random.hs +7/−7
- test/Test/Crypto/Secretbox.hs +0/−32
- test/Test/Crypto/Sign.hs +36/−0
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