NaCl 0.0.3.1 → 0.0.4.0
raw patch · 30 files changed
+2194/−711 lines, 30 filesdep +base16PVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: base16
API changes (from Hackage documentation)
- Crypto.Box: create :: (ByteArrayAccess nonceBytes, ByteArrayAccess ptBytes, ByteArray ctBytes) => PublicKey -> SecretKey -> Nonce nonceBytes -> ptBytes -> ctBytes
- Crypto.Box: keypair :: IO (PublicKey, SecretKey)
- Crypto.Box: open :: (ByteArrayAccess nonceBytes, ByteArray ptBytes, ByteArrayAccess ctBytes) => SecretKey -> PublicKey -> Nonce nonceBytes -> ctBytes -> Maybe ptBytes
- Crypto.Box: toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)
- Crypto.Box: toPublicKey :: ByteString -> Maybe PublicKey
- Crypto.Box: toSecretKey :: ScrubbedBytes -> Maybe SecretKey
- Crypto.Box: type Nonce a = SizedByteArray CRYPTO_BOX_NONCEBYTES a
- Crypto.Box: type PublicKey = SizedByteArray CRYPTO_BOX_PUBLICKEYBYTES ByteString
- Crypto.Box: type SecretKey = SizedByteArray CRYPTO_BOX_SECRETKEYBYTES ScrubbedBytes
- Crypto.Box.Internal: create :: (ByteArrayAccess nonce, ByteArrayAccess pt, ByteArray ct) => PublicKey -> SecretKey -> Nonce nonce -> pt -> IO ct
- Crypto.Box.Internal: keypair :: IO (PublicKey, SecretKey)
- Crypto.Box.Internal: open :: (ByteArrayAccess nonce, ByteArray pt, ByteArrayAccess ct) => SecretKey -> PublicKey -> Nonce nonce -> ct -> IO (Maybe pt)
- Crypto.Box.Internal: toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)
- Crypto.Box.Internal: toPublicKey :: ByteString -> Maybe PublicKey
- Crypto.Box.Internal: toSecretKey :: ScrubbedBytes -> Maybe SecretKey
- Crypto.Box.Internal: type Nonce a = SizedByteArray CRYPTO_BOX_NONCEBYTES a
- Crypto.Box.Internal: type PublicKey = SizedByteArray CRYPTO_BOX_PUBLICKEYBYTES ByteString
- Crypto.Box.Internal: type SecretKey = SizedByteArray CRYPTO_BOX_SECRETKEYBYTES ScrubbedBytes
- Crypto.Secretbox: create :: (ByteArrayAccess keyBytes, ByteArrayAccess nonceBytes, ByteArrayAccess ptBytes, ByteArray ctBytes) => Key keyBytes -> Nonce nonceBytes -> ptBytes -> ctBytes
- Crypto.Secretbox: open :: (ByteArrayAccess keyBytes, ByteArrayAccess nonceBytes, ByteArray ptBytes, ByteArrayAccess ctBytes) => Key keyBytes -> Nonce nonceBytes -> ctBytes -> Maybe ptBytes
- Crypto.Secretbox: toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)
- Crypto.Secretbox: toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)
- Crypto.Secretbox: type Key a = SizedByteArray CRYPTO_SECRETBOX_KEYBYTES a
- Crypto.Secretbox: type Nonce a = SizedByteArray CRYPTO_SECRETBOX_NONCEBYTES a
- Crypto.Secretbox.Internal: create :: (ByteArrayAccess key, ByteArrayAccess nonce, ByteArrayAccess pt, ByteArray ct) => Key key -> Nonce nonce -> pt -> IO ct
- Crypto.Secretbox.Internal: open :: (ByteArrayAccess key, ByteArrayAccess nonce, ByteArray pt, ByteArrayAccess ct) => Key key -> Nonce nonce -> ct -> IO (Maybe pt)
- Crypto.Secretbox.Internal: toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)
- Crypto.Secretbox.Internal: toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)
- Crypto.Secretbox.Internal: type Key a = SizedByteArray CRYPTO_SECRETBOX_KEYBYTES a
- Crypto.Secretbox.Internal: type Nonce a = SizedByteArray CRYPTO_SECRETBOX_NONCEBYTES a
+ NaCl.Auth: create :: (ByteArray authBytes, ByteArrayAccess keyBytes, ByteArrayAccess msg) => Key keyBytes -> msg -> Authenticator authBytes
+ NaCl.Auth: toAuthenticator :: ByteArrayAccess ba => ba -> Maybe (Authenticator ba)
+ NaCl.Auth: toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)
+ NaCl.Auth: type Authenticator a = SizedByteArray CRYPTO_AUTH_BYTES a
+ NaCl.Auth: type Key a = SizedByteArray CRYPTO_AUTH_KEYBYTES a
+ NaCl.Auth: verify :: (ByteArrayAccess authBytes, ByteArrayAccess msg, ByteArrayAccess keyBytes) => Key keyBytes -> msg -> Authenticator authBytes -> Bool
+ NaCl.Auth.Internal: create :: (ByteArrayAccess keyBytes, ByteArrayAccess msg, ByteArray authBytes) => Key keyBytes -> msg -> IO (Authenticator authBytes)
+ NaCl.Auth.Internal: toAuthenticator :: ByteArrayAccess ba => ba -> Maybe (Authenticator ba)
+ NaCl.Auth.Internal: toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)
+ NaCl.Auth.Internal: type Authenticator a = SizedByteArray CRYPTO_AUTH_BYTES a
+ NaCl.Auth.Internal: type Key a = SizedByteArray CRYPTO_AUTH_KEYBYTES a
+ NaCl.Auth.Internal: verify :: (ByteArrayAccess keyBytes, ByteArrayAccess msg, ByteArrayAccess authBytes) => Key keyBytes -> msg -> Authenticator authBytes -> IO Bool
+ NaCl.Box: create :: (ByteArrayAccess pkBytes, ByteArrayAccess skBytes, ByteArrayAccess nonceBytes, ByteArrayAccess ptBytes, ByteArray ctBytes) => PublicKey pkBytes -> SecretKey skBytes -> Nonce nonceBytes -> ptBytes -> ctBytes
+ NaCl.Box: keypair :: IO (PublicKey ByteString, SecretKey ScrubbedBytes)
+ NaCl.Box: open :: (ByteArrayAccess skBytes, ByteArrayAccess pkBytes, ByteArrayAccess nonceBytes, ByteArray ptBytes, ByteArrayAccess ctBytes) => SecretKey skBytes -> PublicKey pkBytes -> Nonce nonceBytes -> ctBytes -> Maybe ptBytes
+ NaCl.Box: toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)
+ NaCl.Box: toPublicKey :: ByteArrayAccess bytes => bytes -> Maybe (PublicKey bytes)
+ NaCl.Box: toSecretKey :: ByteArrayAccess bytes => bytes -> Maybe (SecretKey bytes)
+ NaCl.Box: type Nonce a = SizedByteArray CRYPTO_BOX_NONCEBYTES a
+ NaCl.Box: type PublicKey a = SizedByteArray CRYPTO_BOX_PUBLICKEYBYTES a
+ NaCl.Box: type SecretKey a = SizedByteArray CRYPTO_BOX_SECRETKEYBYTES a
+ NaCl.Box.Internal: create :: (ByteArrayAccess pkBytes, ByteArrayAccess skBytes, ByteArrayAccess nonce, ByteArrayAccess pt, ByteArray ct) => PublicKey pkBytes -> SecretKey skBytes -> Nonce nonce -> pt -> IO ct
+ NaCl.Box.Internal: keypair :: IO (PublicKey ByteString, SecretKey ScrubbedBytes)
+ NaCl.Box.Internal: open :: (ByteArrayAccess skBytes, ByteArrayAccess pkBytes, ByteArrayAccess nonce, ByteArray pt, ByteArrayAccess ct) => SecretKey skBytes -> PublicKey pkBytes -> Nonce nonce -> ct -> IO (Maybe pt)
+ NaCl.Box.Internal: toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)
+ NaCl.Box.Internal: toPublicKey :: ByteArrayAccess bytes => bytes -> Maybe (PublicKey bytes)
+ NaCl.Box.Internal: toSecretKey :: ByteArrayAccess bytes => bytes -> Maybe (SecretKey bytes)
+ NaCl.Box.Internal: type Nonce a = SizedByteArray CRYPTO_BOX_NONCEBYTES a
+ NaCl.Box.Internal: type PublicKey a = SizedByteArray CRYPTO_BOX_PUBLICKEYBYTES a
+ NaCl.Box.Internal: type SecretKey a = SizedByteArray CRYPTO_BOX_SECRETKEYBYTES a
+ NaCl.Hash: sha256 :: (ByteArrayAccess pt, ByteArray hashBytes) => pt -> HashSha256 hashBytes
+ NaCl.Hash: sha512 :: (ByteArrayAccess pt, ByteArray hashBytes) => pt -> HashSha512 hashBytes
+ NaCl.Hash: type HashSha256 a = SizedByteArray CRYPTO_HASH_SHA256_BYTES a
+ NaCl.Hash: type HashSha512 a = SizedByteArray CRYPTO_HASH_SHA512_BYTES a
+ NaCl.Hash.Internal: sha256 :: (ByteArrayAccess pt, ByteArray hashBytes) => pt -> IO (HashSha256 hashBytes)
+ NaCl.Hash.Internal: sha512 :: (ByteArrayAccess pt, ByteArray hashBytes) => pt -> IO (HashSha512 hashBytes)
+ NaCl.Hash.Internal: type HashSha256 a = SizedByteArray CRYPTO_HASH_SHA256_BYTES a
+ NaCl.Hash.Internal: type HashSha512 a = SizedByteArray CRYPTO_HASH_SHA512_BYTES a
+ NaCl.Scalarmult: Point :: SizedByteArray CRYPTO_SCALARMULT_BYTES a -> Point a
+ NaCl.Scalarmult: Scalar :: SizedByteArray CRYPTO_SCALARMULT_SCALARBYTES a -> Scalar a
+ NaCl.Scalarmult: instance Data.ByteArray.Types.ByteArray a => Data.ByteArray.Sized.ByteArrayN Libsodium.Constants.CRYPTO_SCALARMULT_BYTES (NaCl.Scalarmult.Point a)
+ NaCl.Scalarmult: instance Data.ByteArray.Types.ByteArray a => Data.ByteArray.Sized.ByteArrayN Libsodium.Constants.CRYPTO_SCALARMULT_SCALARBYTES (NaCl.Scalarmult.Scalar a)
+ NaCl.Scalarmult: instance Data.ByteArray.Types.ByteArrayAccess a => Data.ByteArray.Types.ByteArrayAccess (NaCl.Scalarmult.Point a)
+ NaCl.Scalarmult: instance Data.ByteArray.Types.ByteArrayAccess a => Data.ByteArray.Types.ByteArrayAccess (NaCl.Scalarmult.Scalar a)
+ NaCl.Scalarmult: instance GHC.Classes.Eq a => GHC.Classes.Eq (NaCl.Scalarmult.Point a)
+ NaCl.Scalarmult: instance GHC.Classes.Eq a => GHC.Classes.Eq (NaCl.Scalarmult.Scalar a)
+ NaCl.Scalarmult: instance GHC.Classes.Ord a => GHC.Classes.Ord (NaCl.Scalarmult.Point a)
+ NaCl.Scalarmult: instance GHC.Classes.Ord a => GHC.Classes.Ord (NaCl.Scalarmult.Scalar a)
+ NaCl.Scalarmult: instance GHC.Show.Show a => GHC.Show.Show (NaCl.Scalarmult.Point a)
+ NaCl.Scalarmult: instance GHC.Show.Show a => GHC.Show.Show (NaCl.Scalarmult.Scalar a)
+ NaCl.Scalarmult: mult :: forall outBytes pointBytes scalarBytes. (ByteArrayAccess pointBytes, ByteArrayAccess scalarBytes, ByteArray outBytes) => Point pointBytes -> Scalar scalarBytes -> Maybe (Point outBytes)
+ NaCl.Scalarmult: multBase :: forall outBytes scalarBytes. (ByteArrayAccess scalarBytes, ByteArray outBytes) => Scalar scalarBytes -> Point outBytes
+ NaCl.Scalarmult: newtype Point a
+ NaCl.Scalarmult: newtype Scalar a
+ NaCl.Scalarmult: toPoint :: ByteArrayAccess bytes => bytes -> Maybe (Point bytes)
+ NaCl.Scalarmult: toScalar :: ByteArrayAccess bytes => bytes -> Maybe (Scalar bytes)
+ NaCl.Secretbox: create :: (ByteArrayAccess keyBytes, ByteArrayAccess nonceBytes, ByteArrayAccess ptBytes, ByteArray ctBytes) => Key keyBytes -> Nonce nonceBytes -> ptBytes -> ctBytes
+ NaCl.Secretbox: open :: (ByteArrayAccess keyBytes, ByteArrayAccess nonceBytes, ByteArray ptBytes, ByteArrayAccess ctBytes) => Key keyBytes -> Nonce nonceBytes -> ctBytes -> Maybe ptBytes
+ NaCl.Secretbox: toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)
+ NaCl.Secretbox: toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)
+ NaCl.Secretbox: type Key a = SizedByteArray CRYPTO_SECRETBOX_KEYBYTES a
+ NaCl.Secretbox: type Nonce a = SizedByteArray CRYPTO_SECRETBOX_NONCEBYTES a
+ NaCl.Secretbox.Internal: create :: (ByteArrayAccess key, ByteArrayAccess nonce, ByteArrayAccess pt, ByteArray ct) => Key key -> Nonce nonce -> pt -> IO ct
+ NaCl.Secretbox.Internal: open :: (ByteArrayAccess key, ByteArrayAccess nonce, ByteArray pt, ByteArrayAccess ct) => Key key -> Nonce nonce -> ct -> IO (Maybe pt)
+ NaCl.Secretbox.Internal: toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)
+ NaCl.Secretbox.Internal: toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)
+ NaCl.Secretbox.Internal: type Key a = SizedByteArray CRYPTO_SECRETBOX_KEYBYTES a
+ NaCl.Secretbox.Internal: type Nonce a = SizedByteArray CRYPTO_SECRETBOX_NONCEBYTES a
+ NaCl.Sign: create :: (ByteArrayAccess skBytes, ByteArrayAccess ptBytes, ByteArray ctBytes) => SecretKey skBytes -> ptBytes -> ctBytes
+ NaCl.Sign: keypair :: IO (PublicKey ByteString, SecretKey ScrubbedBytes)
+ NaCl.Sign: open :: (ByteArrayAccess pkBytes, ByteArray ptBytes, ByteArrayAccess ctBytes) => PublicKey pkBytes -> ctBytes -> Maybe ptBytes
+ NaCl.Sign: toPublicKey :: ByteArrayAccess bytes => bytes -> Maybe (PublicKey bytes)
+ NaCl.Sign: toSecretKey :: ByteArrayAccess bytes => bytes -> Maybe (SecretKey bytes)
+ NaCl.Sign: type PublicKey a = SizedByteArray CRYPTO_SIGN_PUBLICKEYBYTES a
+ NaCl.Sign: type SecretKey a = SizedByteArray CRYPTO_SIGN_SECRETKEYBYTES a
+ NaCl.Sign.Internal: create :: (ByteArrayAccess skBytes, ByteArrayAccess pt, ByteArray ct) => SecretKey skBytes -> pt -> IO ct
+ NaCl.Sign.Internal: keypair :: IO (PublicKey ByteString, SecretKey ScrubbedBytes)
+ NaCl.Sign.Internal: open :: (ByteArrayAccess pkBytes, ByteArray pt, ByteArrayAccess ct) => PublicKey pkBytes -> ct -> IO (Maybe pt)
+ NaCl.Sign.Internal: toPublicKey :: ByteArrayAccess bytes => bytes -> Maybe (PublicKey bytes)
+ NaCl.Sign.Internal: toSecretKey :: ByteArrayAccess bytes => bytes -> Maybe (SecretKey bytes)
+ NaCl.Sign.Internal: type PublicKey a = SizedByteArray CRYPTO_SIGN_PUBLICKEYBYTES a
+ NaCl.Sign.Internal: type SecretKey a = SizedByteArray CRYPTO_SIGN_SECRETKEYBYTES a
+ NaCl.Stream: generate :: forall n key nonce ct. (ByteArrayAccess key, ByteArrayAccess nonce, ByteArrayN n ct, n <= MaxStreamSize) => Key key -> Nonce nonce -> ct
+ NaCl.Stream: toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)
+ NaCl.Stream: toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)
+ NaCl.Stream: type Key a = SizedByteArray CRYPTO_STREAM_KEYBYTES a
+ NaCl.Stream: type MaxStreamSize = 18446744073709551615
+ NaCl.Stream: type Nonce a = SizedByteArray CRYPTO_STREAM_NONCEBYTES a
+ NaCl.Stream: xor :: (ByteArrayAccess key, ByteArrayAccess nonce, ByteArrayAccess pt, ByteArray ct) => Key key -> Nonce nonce -> pt -> ct
+ NaCl.Stream.Internal: generate :: forall key nonce n ct. (ByteArrayAccess key, ByteArrayAccess nonce, ByteArrayN n ct, n <= MaxStreamSize) => Key key -> Nonce nonce -> IO ct
+ NaCl.Stream.Internal: toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)
+ NaCl.Stream.Internal: toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)
+ NaCl.Stream.Internal: type Key a = SizedByteArray CRYPTO_STREAM_KEYBYTES a
+ NaCl.Stream.Internal: type MaxStreamSize = 18446744073709551615
+ NaCl.Stream.Internal: type Nonce a = SizedByteArray CRYPTO_STREAM_NONCEBYTES a
+ NaCl.Stream.Internal: xor :: (ByteArrayAccess key, ByteArrayAccess nonce, ByteArrayAccess pt, ByteArray ct) => Key key -> Nonce nonce -> pt -> IO ct
+ NaCl.Verify: class CryptoVerify n => NaClComparable n
+ NaCl.Verify: eq :: forall n xBytes yBytes. (NaClComparable n, ByteArrayAccess xBytes, ByteArrayAccess yBytes) => SizedByteArray n xBytes -> SizedByteArray n yBytes -> Bool
+ NaCl.Verify: instance NaCl.Verify.CryptoVerify 16
+ NaCl.Verify: instance NaCl.Verify.CryptoVerify 32
+ NaCl.Verify: instance NaCl.Verify.NaClComparable 16
+ NaCl.Verify: instance NaCl.Verify.NaClComparable 32
Files
- CHANGELOG.md +6/−0
- NaCl.cabal +70/−18
- lib/Crypto/Box.hs +0/−108
- lib/Crypto/Box/Internal.hs +0/−139
- lib/Crypto/Secretbox.hs +0/−110
- lib/Crypto/Secretbox/Internal.hs +0/−110
- lib/NaCl/Auth.hs +86/−0
- lib/NaCl/Auth/Internal.hs +91/−0
- lib/NaCl/Box.hs +115/−0
- lib/NaCl/Box/Internal.hs +148/−0
- lib/NaCl/Hash.hs +65/−0
- lib/NaCl/Hash/Internal.hs +66/−0
- lib/NaCl/Scalarmult.hs +102/−0
- lib/NaCl/Secretbox.hs +112/−0
- lib/NaCl/Secretbox/Internal.hs +109/−0
- lib/NaCl/Sign.hs +83/−0
- lib/NaCl/Sign/Internal.hs +116/−0
- lib/NaCl/Stream.hs +68/−0
- lib/NaCl/Stream/Internal.hs +112/−0
- lib/NaCl/Verify.hs +69/−0
- test/Test/Crypto/Box.hs +0/−113
- test/Test/Crypto/Secretbox.hs +0/−113
- test/Test/NaCl/Auth.hs +63/−0
- test/Test/NaCl/Box.hs +113/−0
- test/Test/NaCl/Hash.hs +60/−0
- test/Test/NaCl/Scalarmult.hs +97/−0
- test/Test/NaCl/Secretbox.hs +113/−0
- test/Test/NaCl/Sign.hs +199/−0
- test/Test/NaCl/Stream.hs +78/−0
- test/Test/NaCl/Verify.hs +53/−0
CHANGELOG.md view
@@ -7,3 +7,9 @@ * `Sized` byte arrays * `Secretbox` * `Box`+* `Auth`+* `Sign`+* `Stream`+* `Hash`+* `Verify`+* `Scalarmult`
NaCl.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: 1a62f7dc222dc8ad85a2dec5e6789c2ea3adaa30dbbfcb5b89a40e1c62f4126a+-- hash: 96801ddcac1bde4bd9ab56c0f221a68ee0d9ccdfe421b3c1c2aeaa46a72b99ba name: NaCl-version: 0.0.3.1+version: 0.0.4.0 synopsis: Easy-and-safe-to-use high-level Haskell bindings to NaCl description: This library uses <https://libsodium.org Sodium> under the hood, but only exposes the primitives that are part of the “classic” NaCl@@ -26,19 +26,28 @@ If you need them too (you probably do), you should use </package/crypto-sodium crypto-sodium> instead. .+ = Public-key cryptography+ .+ * Authenticated encryption: "NaCl.Box"+ * Scalar multiplication: "NaCl.Scalarmult"+ * Public-key signatures: "NaCl.Sign"+ . = Secret-key cryptography .- * Authenticated encryption: "Crypto.Secretbox"+ * Authenticated encryption: "NaCl.Secretbox"+ * Unauthenticated streaming encryption: "NaCl.Stream"+ * Authentication: "NaCl.Auth" .- = Public-key cryptography+ = Low-level functions .- * Authenticated encryption: "Crypto.Box"+ * Hashing: "NaCl.Hash"+ * String comparison: "NaCl.Verify" category: Cryptography 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@@ -52,34 +61,76 @@ library exposed-modules:- Crypto.Box- Crypto.Box.Internal- Crypto.Secretbox- Crypto.Secretbox.Internal+ NaCl.Auth+ NaCl.Auth.Internal+ NaCl.Box+ NaCl.Box.Internal+ NaCl.Hash+ NaCl.Hash.Internal+ NaCl.Scalarmult+ NaCl.Secretbox+ NaCl.Secretbox.Internal+ NaCl.Sign+ NaCl.Sign.Internal+ NaCl.Stream+ NaCl.Stream.Internal+ NaCl.Verify other-modules: Paths_NaCl 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: base >=4.10 && <4.15 , bytestring >=0.9 && <0.11 , libsodium >=1.0.11 && <2 , memory >=0.14.15 && <0.16- , safe-exceptions >=0.1 && <0.2+ , safe-exceptions ==0.1.* default-language: Haskell2010 test-suite test type: exitcode-stdio-1.0 main-is: Test.hs other-modules:- Test.Crypto.Box- Test.Crypto.Secretbox+ Test.NaCl.Auth+ Test.NaCl.Box+ Test.NaCl.Hash+ Test.NaCl.Scalarmult+ Test.NaCl.Secretbox+ Test.NaCl.Sign+ Test.NaCl.Stream+ Test.NaCl.Verify Paths_NaCl 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@@ -87,11 +138,12 @@ HUnit , NaCl , base >=4.10 && <4.15+ , base16 >=0.1.1 && <0.4 , bytestring >=0.9 && <0.11 , 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/Box.hs
@@ -1,108 +0,0 @@--- SPDX-FileCopyrightText: 2020 Serokell------ SPDX-License-Identifier: MPL-2.0---- | Public-key authenticated encryption.------ It is best to import this module qualified:------ @--- import qualified Crypto.Box as Box------ encrypted = Box.'create' pk sk nonce message--- decrypted = Box.'open' pk sk nonce encrypted--- @------ This is @crypto_box_*@ from NaCl.-module Crypto.Box- ( PublicKey- , toPublicKey- , SecretKey- , toSecretKey- , keypair-- , Nonce- , toNonce-- , create- , open- ) where--import Data.ByteArray (ByteArray, ByteArrayAccess)-import System.IO.Unsafe (unsafeDupablePerformIO)--import Crypto.Box.Internal (Nonce, PublicKey, SecretKey, keypair, toNonce, toPublicKey, toSecretKey)--import qualified Crypto.Box.Internal as I----- | 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 if you encrypt the same--- message with the same key multiple times, you will get different ciphertexts,--- which is required for--- <https://en.wikipedia.org/wiki/Semantic_security semantic security>.--- There are two standard ways of getting it:------ 1. /Use a counter/. In this case you keep a counter of encrypted messages,--- which means that the nonce will be new for each new message.------ 2. /Random/. You generate a random nonce every time you encrypt a message.--- Since the nonce is large enough, the chances of you using the same--- nonce twice are negligible. For useful helpers, see @Crypto.Random@,--- in <https://hackage.haskell.org/package/crypto-sodium crypto-sodium>.------ * @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 pk sk nonce msg =- -- This IO is safe, because it is pure.- unsafeDupablePerformIO $ I.create pk sk nonce msg----- | Decrypt a message.------ @--- decrypted = Box.open sk pk nonce encrypted--- @------ * @sk@ is the receiver’s secret key, used for description.--- * @pk@ is the sender’s public key, used for authentication.--- * @nonce@ is the same that was 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.-open- :: ( ByteArrayAccess nonceBytes- , ByteArray ptBytes, ByteArrayAccess ctBytes- )- => SecretKey -- ^ Receiver’s secret key- -> PublicKey -- ^ Sender’s public key- -> Nonce nonceBytes -- ^ Nonce- -> ctBytes -- ^ Encrypted message (cyphertext)- -> Maybe ptBytes-open sk pk nonce ct =- -- This IO is safe, because it is pure.- unsafeDupablePerformIO $ I.open sk pk nonce ct
− lib/Crypto/Box/Internal.hs
@@ -1,139 +0,0 @@--- SPDX-FileCopyrightText: 2020 Serokell------ SPDX-License-Identifier: MPL-2.0---- | Internals of @crypto_box@.-module Crypto.Box.Internal- ( SecretKey- , toSecretKey- , PublicKey- , toPublicKey- , keypair-- , Nonce- , toNonce-- , create- , open- ) where--import Prelude hiding (length)--import Data.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes, allocRet, length, withByteArray)-import Data.ByteArray.Sized (SizedByteArray, sizedByteArray)-import Data.ByteString (ByteString)-import Data.Functor (void)-import Data.Proxy (Proxy (Proxy))--import qualified Data.ByteArray.Sized as Sized (alloc, allocRet)-import qualified Libsodium as Na----- | Secret key that can be used for Box.-type SecretKey = SizedByteArray Na.CRYPTO_BOX_SECRETKEYBYTES ScrubbedBytes---- | Convert bytes to a secret key.-toSecretKey :: ScrubbedBytes -> Maybe SecretKey-toSecretKey = sizedByteArray---- | Public key that can be used for Box.-type PublicKey = SizedByteArray Na.CRYPTO_BOX_PUBLICKEYBYTES ByteString---- | Convert bytes to a public key.-toPublicKey :: ByteString -> Maybe PublicKey-toPublicKey = sizedByteArray---- | Generate a new 'SecretKey' together with its 'PublicKey'.------ Note: this function is not thread-safe (since the underlying--- C function is not thread-safe both in Sodium and in NaCl)!--- Either make sure there are no concurrent calls or see--- @Crypto.Init@ in--- <https://hackage.haskell.org/package/crypto-sodium crypto-sodium>--- to learn how to make this function thread-safe.-keypair :: IO (PublicKey, SecretKey)-keypair = do- (pk, sk) <-- Sized.allocRet Proxy $ \skPtr ->- Sized.alloc $ \pkPtr ->- -- always returns 0, so we don’t check it- void $ Na.crypto_box_keypair pkPtr skPtr- pure (pk, sk)----- | Nonce that can be used for Box.------ This type is parametrised by the actual data type that contains--- bytes. This can be, for example, a @ByteString@.-type Nonce a = SizedByteArray Na.CRYPTO_BOX_NONCEBYTES a---- | Make a 'Nonce' 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 nonce with a Box.-toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)-toNonce = sizedByteArray----- | Encrypt a message.-create- :: ( ByteArrayAccess nonce- , ByteArrayAccess pt, ByteArray ct- )- => PublicKey -- ^ Receiver’s public key- -> SecretKey -- ^ Sender’s secret key- -> Nonce nonce -- ^ Nonce- -> pt -- ^ Plaintext message- -> IO ct-create pk sk nonce msg = do- (_ret, ct) <-- allocRet clen $ \ctPtr ->- withByteArray pk $ \pkPtr ->- withByteArray sk $ \skPtr ->- withByteArray nonce $ \noncePtr ->- withByteArray msg $ \msgPtr -> do- -- TODO: Maybe, reimplement this without _easy, to stay closer- -- to the original NaCl.- Na.crypto_box_easy ctPtr- msgPtr (fromIntegral $ length msg)- noncePtr- pkPtr skPtr- -- _ret can be only 0, so we don’t check it- -- TODO: Actually, it looks like this function can fail and return- -- a -1, even though this is not documented :/.- pure ct- where- clen :: Int- clen = fromIntegral Na.crypto_box_macbytes + length msg----- | Decrypt a message.-open- :: ( ByteArrayAccess nonce- , ByteArray pt, ByteArrayAccess ct- )- => SecretKey -- ^ Receiver’s secret key- -> PublicKey -- ^ Sender’s public key- -> Nonce nonce -- ^ Nonce- -> ct -- ^ Cyphertext- -> IO (Maybe pt)-open sk pk nonce ct = do- (ret, msg) <-- allocRet mlen $ \msgPtr ->- withByteArray sk $ \skPtr ->- withByteArray pk $ \pkPtr ->- withByteArray nonce $ \noncePtr ->- withByteArray ct $ \ctPtr -> do- -- TODO: Maybe, reimplement this without _easy, to stay closer- -- to the original NaCl.- Na.crypto_box_open_easy msgPtr- ctPtr (fromIntegral $ length ct)- noncePtr- pkPtr skPtr- if ret == 0 then- pure $ Just msg- else- pure Nothing- where- mlen :: Int- mlen = length ct - fromIntegral Na.crypto_box_macbytes
− lib/Crypto/Secretbox.hs
@@ -1,110 +0,0 @@--- SPDX-FileCopyrightText: 2020 Serokell------ SPDX-License-Identifier: MPL-2.0---- | Symmetric authenticated encryption.------ It is best to import this module qualified:------ @--- import qualified Crypto.Secretbox as Secretbox------ encrypted = Secretbox.'create' key nonce message--- decrypted = Secretbox.'open' key nonce encrypted--- @------ This is @crypto_secretbox_*@ from NaCl.-module Crypto.Secretbox- ( Key- , toKey-- , Nonce- , toNonce-- , create- , open- ) where--import Data.ByteArray (ByteArray, ByteArrayAccess)-import System.IO.Unsafe (unsafeDupablePerformIO)--import Crypto.Secretbox.Internal (Key, Nonce, toKey, toNonce)--import qualified Crypto.Secretbox.Internal as I----- | Encrypt a message.------ @--- encrypted = Secretbox.create key nonce message--- @------ * @key@ is the secret key used for encryption. There are two typical ways--- of creating it:------ 1. /Derive from a password/. If you want to protect a message with a password,--- you must use a--- <https://en.wikipedia.org/wiki/Key_derivation_function key derivation function>--- to turn this password into an encryption key.------ 2. /Generate a random one/. This can be useful in certain situations when--- you want to have an intermediate key that you will encrypt and share--- later.------ The @Crypto.Key@ module in--- <https://hackage.haskell.org/package/crypto-sodium crypto-sodium>--- has functions to help in either case.------ * @nonce@ is an extra noise that ensures that if you encrypt the same--- message with the same key multiple times, you will get different ciphertexts,--- which is required for--- <https://en.wikipedia.org/wiki/Semantic_security semantic security>.--- There are two standard ways of getting it:------ 1. /Use a counter/. In this case you keep a counter of encrypted messages,--- which means that the nonce will be new for each new message.------ 2. /Random/. You generate a random nonce every time you encrypt a message.--- Since the nonce is large enough, the chances of you using the same--- nonce twice are negligible. For useful helpers, see @Crypto.Random@,--- in <https://hackage.haskell.org/package/crypto-sodium crypto-sodium>.------ * @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 key nonce msg =- -- This IO is safe, because it is pure.- unsafeDupablePerformIO $ I.create key nonce msg----- | Decrypt a message.------ @--- decrypted = Secretbox.open 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.-open- :: ( ByteArrayAccess keyBytes, ByteArrayAccess nonceBytes- , ByteArray ptBytes, ByteArrayAccess ctBytes- )- => Key keyBytes -- ^ Secret key- -> Nonce nonceBytes -- ^ Nonce- -> ctBytes -- ^ Encrypted message (cyphertext)- -> Maybe ptBytes-open key nonce ct =- -- This IO is safe, because it is pure.- unsafeDupablePerformIO $ I.open key nonce ct
− lib/Crypto/Secretbox/Internal.hs
@@ -1,110 +0,0 @@--- SPDX-FileCopyrightText: 2020 Serokell------ SPDX-License-Identifier: MPL-2.0---- | Internals of @crypto_secretbox@.-module Crypto.Secretbox.Internal- ( Key- , toKey-- , Nonce- , toNonce-- , create- , open- ) where--import Prelude hiding (length)--import Data.ByteArray (ByteArray, ByteArrayAccess, allocRet, length, withByteArray)--import qualified Libsodium as Na--import Data.ByteArray.Sized (SizedByteArray, sizedByteArray)----- | Encryption key that can be used for Secretbox.------ 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_SECRETBOX_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 Secretbox.-toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)-toKey = sizedByteArray----- | Nonce that can be used for Secretbox.------ This type is parametrised by the actual data type that contains--- bytes. This can be, for example, a @ByteString@.-type Nonce a = SizedByteArray Na.CRYPTO_SECRETBOX_NONCEBYTES a---- | Make a 'Nonce' 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 nonce with a Secretbox.-toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)-toNonce = sizedByteArray----- | Encrypt a message.-create- :: ( ByteArrayAccess key, ByteArrayAccess nonce- , ByteArrayAccess pt, ByteArray ct- )- => Key key -- ^ Secret key- -> Nonce nonce -- ^ Nonce- -> pt -- ^ Plaintext message- -> IO ct-create key nonce msg = do- (_ret, ct) <-- allocRet clen $ \ctPtr ->- withByteArray key $ \keyPtr ->- withByteArray nonce $ \noncePtr ->- withByteArray msg $ \msgPtr -> do- -- TODO: Maybe, reimplement this without _easy, to stay closer- -- to the original NaCl.- Na.crypto_secretbox_easy ctPtr- msgPtr (fromIntegral $ length msg)- noncePtr- keyPtr- -- _ret can be only 0, so we don’t check it- pure ct- where- clen :: Int- clen = fromIntegral Na.crypto_secretbox_macbytes + length msg----- | Decrypt a message.-open- :: ( ByteArrayAccess key, ByteArrayAccess nonce- , ByteArray pt, ByteArrayAccess ct- )- => Key key -- ^ Secret key- -> Nonce nonce -- ^ Nonce- -> ct -- ^ Cyphertext- -> IO (Maybe pt)-open key nonce ct = do- (ret, msg) <-- allocRet mlen $ \msgPtr ->- withByteArray key $ \keyPtr ->- withByteArray nonce $ \noncePtr ->- withByteArray ct $ \ctPtr -> do- -- TODO: Maybe, reimplement this without _easy, to stay closer- -- to the original NaCl.- Na.crypto_secretbox_open_easy msgPtr- ctPtr (fromIntegral $ length ct)- noncePtr- keyPtr- if ret == 0 then- pure $ Just msg- else- pure Nothing- where- mlen :: Int- mlen = length ct - fromIntegral Na.crypto_secretbox_macbytes
+ lib/NaCl/Auth.hs view
@@ -0,0 +1,86 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | Symmetric message authentication.+--+-- It is best to import this module qualified:+--+-- @+-- import qualified NaCl.Auth as Auth+--+-- authenticator = Auth.'create' key message+-- if Secretbox.'verify' key message authenticator+-- then {- Ok! -}+-- else {- Fail! -}+-- @+--+-- This is @crypto_auth_*@ from NaCl.+module NaCl.Auth+ ( Key+ , toKey++ , Authenticator+ , toAuthenticator++ , create+ , verify+ ) where++import Data.ByteArray (ByteArray, ByteArrayAccess)+import System.IO.Unsafe (unsafePerformIO)++import NaCl.Auth.Internal (Key, Authenticator, toAuthenticator, toKey)++import qualified NaCl.Auth.Internal as I+++-- | Create an authenticator for a message.+--+-- @+-- authenticator = Auth.create key message+-- @+--+-- * @key@ is the secret key used for authentication.+-- See "NaCl.Secretbox" for how to crete it, as the idea is the same.+--+-- * @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 key msg = do+ -- This IO is safe, because it is pure.+ unsafePerformIO $ I.create key msg+++-- | Verify an authenticator for a message.+--+-- @+-- isValid = Auth.verify key message authenticator+-- @+--+-- * @key@ and @message@ are the same as when creating the authenticator.+-- * @authenticator@ is the output of 'create'.+--+-- This function will return @False@ if the message is not exactly the same+-- as it was when the authenticator was created.+verify+ :: ( ByteArrayAccess authBytes+ , ByteArrayAccess msg+ , ByteArrayAccess keyBytes+ )+ => Key keyBytes -- ^ Secret key.+ -> msg -- ^ Authenticated message.+ -> Authenticator authBytes -- ^ Authenticator tag.+ -> Bool+verify key msg auth = do+ -- This IO is safe, because it is pure.+ unsafePerformIO $ I.verify key msg auth
+ lib/NaCl/Auth/Internal.hs view
@@ -0,0 +1,91 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | Internals of @crypto_auth@.+module NaCl.Auth.Internal+ ( Key+ , toKey++ , Authenticator+ , toAuthenticator++ , create+ , verify+ ) where++import Prelude hiding (length)++import Data.ByteArray (ByteArray, ByteArrayAccess, length, withByteArray)+import Data.ByteArray.Sized (SizedByteArray, allocRet, sizedByteArray)+import Data.Proxy (Proxy (Proxy))++import qualified Libsodium as Na+++-- | Secret key that can be used for Sea authentication.+--+-- 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_AUTH_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 for authentication.+toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)+toKey = sizedByteArray+++-- | A tag that confirms the authenticity of somde data.+type Authenticator a = SizedByteArray Na.CRYPTO_AUTH_BYTES a++-- | Convert raw bytes into an 'Authenticator'.+--+-- This function returns @Just@ if and only if the byte array has+-- the right length to be used as an authenticator.+toAuthenticator :: ByteArrayAccess ba => ba -> Maybe (Authenticator ba)+toAuthenticator = sizedByteArray+++-- | Create an authenticator.+create+ :: ( ByteArrayAccess keyBytes+ , ByteArrayAccess msg+ , ByteArray authBytes+ )+ => Key keyBytes -- ^ Secret key.+ -> msg -- ^ Message to authenticate.+ -> IO (Authenticator authBytes)+create key msg = do+ (_ret, auth) <-+ allocRet (Proxy @Na.CRYPTO_AUTH_BYTES) $ \authPtr ->+ withByteArray key $ \keyPtr ->+ withByteArray msg $ \msgPtr -> do+ Na.crypto_auth authPtr+ msgPtr (fromIntegral $ length msg)+ keyPtr+ -- _ret can be only 0, so we don’t check it+ pure auth+++-- | Verify an authenticator.+verify+ :: ( ByteArrayAccess keyBytes+ , ByteArrayAccess msg+ , ByteArrayAccess authBytes+ )+ => Key keyBytes -- ^ Secret key.+ -> msg -- ^ Authenticated message.+ -> Authenticator authBytes -- ^ Authenticator tag.+ -> IO Bool+verify key msg auth = do+ ret <-+ withByteArray key $ \keyPtr ->+ withByteArray msg $ \msgPtr ->+ withByteArray auth $ \authPtr ->+ Na.crypto_auth_verify authPtr+ msgPtr (fromIntegral $ length msg)+ keyPtr+ pure $ ret == 0
+ lib/NaCl/Box.hs view
@@ -0,0 +1,115 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | Public-key authenticated encryption.+--+-- It is best to import this module qualified:+--+-- @+-- import qualified NaCl.Box as Box+--+-- encrypted = Box.'create' pk sk nonce message+-- decrypted = Box.'open' pk sk nonce encrypted+-- @+--+-- This is @crypto_box_*@ from NaCl.+module NaCl.Box+ ( PublicKey+ , toPublicKey+ , SecretKey+ , toSecretKey+ , keypair++ , Nonce+ , toNonce++ , create+ , open+ ) where++import Data.ByteArray (ByteArray, ByteArrayAccess)+import System.IO.Unsafe (unsafePerformIO)++import NaCl.Box.Internal (Nonce, PublicKey, SecretKey, keypair, toNonce, toPublicKey, toSecretKey)++import qualified NaCl.Box.Internal as I+++-- | 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 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 if you encrypt the same+-- message with the same key multiple times, you will get different ciphertexts,+-- which is required for+-- <https://en.wikipedia.org/wiki/Semantic_security semantic security>.+-- There are two standard ways of getting it:+--+-- 1. /Use a counter/. In this case you keep a counter of encrypted messages,+-- which means that the nonce will be new for each new message.+--+-- 2. /Random/. You generate a random nonce every time you encrypt a message.+-- Since the nonce is large enough, the chances of you using the same+-- nonce twice are negligible. For useful helpers, see @Crypto.Random@,+-- in <https://hackage.haskell.org/package/crypto-sodium crypto-sodium>.+--+-- In either case, you need to be able to provide the same nonce when decrypting,+-- so you should be able to recover it by computation (e.g. in the case of+-- a counter) or you should store it alongside the encrypted data. The nonce+-- is not secret, so it is perfectly ok to store it in plaintext.+--+-- * @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 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+create pk sk nonce msg =+ -- This IO is safe, because it is pure.+ unsafePerformIO $ I.create pk sk nonce msg+++-- | Decrypt a message.+--+-- @+-- decrypted = Box.open sk pk nonce encrypted+-- @+--+-- * @sk@ is the receiver’s secret key, used for description.+-- * @pk@ is the sender’s public key, used for authentication.+-- * @nonce@ is the same that was 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.+open+ :: ( 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+open sk pk nonce ct =+ -- This IO is safe, because it is pure.+ unsafePerformIO $ I.open sk pk nonce ct
+ lib/NaCl/Box/Internal.hs view
@@ -0,0 +1,148 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | Internals of @crypto_box@.+module NaCl.Box.Internal+ ( SecretKey+ , toSecretKey+ , PublicKey+ , toPublicKey+ , keypair++ , Nonce+ , toNonce++ , create+ , open+ ) where++import Prelude hiding (length)++import Data.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes, allocRet, length, withByteArray)+import Data.ByteArray.Sized (SizedByteArray, sizedByteArray)+import Data.ByteString (ByteString)+import Data.Functor (void)+import Data.Proxy (Proxy (Proxy))++import qualified Data.ByteArray.Sized as Sized (alloc, allocRet)+import qualified Libsodium as Na+++-- | Secret key that can be used for Box.+--+-- 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 SecretKey a = SizedByteArray Na.CRYPTO_BOX_SECRETKEYBYTES a++-- | Convert bytes to a secret key.+toSecretKey :: ByteArrayAccess bytes => bytes -> Maybe (SecretKey bytes)+toSecretKey = sizedByteArray++-- | Public key that can be used for Box.+--+-- This type is parametrised by the actual data type that contains+-- bytes. This can be, for example, a @ByteString@.+type PublicKey a = SizedByteArray Na.CRYPTO_BOX_PUBLICKEYBYTES a++-- | Convert bytes to a public key.+toPublicKey :: ByteArrayAccess bytes => bytes -> Maybe (PublicKey bytes)+toPublicKey = sizedByteArray++-- | Generate a new 'SecretKey' together with its 'PublicKey'.+--+-- Note: this function is not thread-safe (since the underlying+-- C function is not thread-safe both in Sodium and in NaCl)!+-- Either make sure there are no concurrent calls or see+-- @Crypto.Init@ in+-- <https://hackage.haskell.org/package/crypto-sodium crypto-sodium>+-- to learn how to make this function thread-safe.+keypair :: IO (PublicKey ByteString, SecretKey ScrubbedBytes)+keypair = do+ (pk, sk) <-+ Sized.allocRet Proxy $ \skPtr ->+ Sized.alloc $ \pkPtr ->+ -- always returns 0, so we don’t check it+ void $ Na.crypto_box_keypair pkPtr skPtr+ pure (pk, sk)+++-- | Nonce that can be used for Box.+--+-- This type is parametrised by the actual data type that contains+-- bytes. This can be, for example, a @ByteString@.+type Nonce a = SizedByteArray Na.CRYPTO_BOX_NONCEBYTES a++-- | Make a 'Nonce' 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 nonce with a Box.+toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)+toNonce = sizedByteArray+++-- | Encrypt a message.+create+ :: ( ByteArrayAccess pkBytes, ByteArrayAccess skBytes+ , ByteArrayAccess nonce+ , ByteArrayAccess pt, ByteArray ct+ )+ => PublicKey pkBytes -- ^ Receiver’s public key+ -> SecretKey skBytes -- ^ Sender’s secret key+ -> Nonce nonce -- ^ Nonce+ -> pt -- ^ Plaintext message+ -> IO ct+create pk sk nonce msg = do+ (_ret, ct) <-+ allocRet clen $ \ctPtr ->+ withByteArray pk $ \pkPtr ->+ withByteArray sk $ \skPtr ->+ withByteArray nonce $ \noncePtr ->+ withByteArray msg $ \msgPtr -> do+ -- TODO: Maybe, reimplement this without _easy, to stay closer+ -- to the original NaCl.+ Na.crypto_box_easy ctPtr+ msgPtr (fromIntegral $ length msg)+ noncePtr+ pkPtr skPtr+ -- _ret can be only 0, so we don’t check it+ -- TODO: Actually, it looks like this function can fail and return+ -- a -1, even though this is not documented :/.+ pure ct+ where+ clen :: Int+ clen = fromIntegral Na.crypto_box_macbytes + length msg+++-- | Decrypt a message.+open+ :: ( ByteArrayAccess skBytes, ByteArrayAccess pkBytes+ , ByteArrayAccess nonce+ , ByteArray pt, ByteArrayAccess ct+ )+ => SecretKey skBytes -- ^ Receiver’s secret key+ -> PublicKey pkBytes -- ^ Sender’s public key+ -> Nonce nonce -- ^ Nonce+ -> ct -- ^ Cyphertext+ -> IO (Maybe pt)+open sk pk nonce ct = do+ (ret, msg) <-+ allocRet mlen $ \msgPtr ->+ withByteArray sk $ \skPtr ->+ withByteArray pk $ \pkPtr ->+ withByteArray nonce $ \noncePtr ->+ withByteArray ct $ \ctPtr -> do+ -- TODO: Maybe, reimplement this without _easy, to stay closer+ -- to the original NaCl.+ Na.crypto_box_open_easy msgPtr+ ctPtr (fromIntegral $ length ct)+ noncePtr+ pkPtr skPtr+ if ret == 0 then+ pure $ Just msg+ else+ pure Nothing+ where+ mlen :: Int+ mlen = length ct - fromIntegral Na.crypto_box_macbytes
+ lib/NaCl/Hash.hs view
@@ -0,0 +1,65 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | Hashing.+--+-- It is best to import this module qualified:+--+-- @+-- import qualified NaCl.Hash as Hash+--+-- hash_sha256 = Hash.'sha256' message+-- hash_sha512 = Hash.'sha512' signed+-- @+--+-- This is @crypto_hash_*@ from NaCl.+module NaCl.Hash+ ( HashSha256+ , sha256++ , HashSha512+ , sha512+ ) where++import Data.ByteArray (ByteArray, ByteArrayAccess)+import System.IO.Unsafe (unsafePerformIO)++import NaCl.Hash.Internal (HashSha256, HashSha512)++import qualified NaCl.Hash.Internal as I+++-- | Hash a message using SHA-256.+--+-- @+-- hash = Hash.'sha256' message+-- @+--+-- * @message@ is the data you are hashing.+sha256+ :: ( ByteArrayAccess pt+ , ByteArray hashBytes+ )+ => pt -- ^ Message to hash+ -> HashSha256 hashBytes+sha256 msg =+ -- This IO is safe, because it is pure.+ unsafePerformIO $ I.sha256 msg++-- | Hash a message using SHA-512.+--+-- @+-- hash = Hash.'sha512' message+-- @+--+-- * @message@ is the data you are hashing.+sha512+ :: ( ByteArrayAccess pt+ , ByteArray hashBytes+ )+ => pt -- ^ Message to hash+ -> HashSha512 hashBytes+sha512 msg =+ -- This IO is safe, because it is pure.+ unsafePerformIO $ I.sha512 msg
+ lib/NaCl/Hash/Internal.hs view
@@ -0,0 +1,66 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | Internals of @crypto_hash@.+module NaCl.Hash.Internal+ ( HashSha256+ , sha256++ , HashSha512+ , sha512+ ) where++import Prelude hiding (length)++import Data.ByteArray (ByteArray, ByteArrayAccess, length, withByteArray)+import Data.ByteArray.Sized (SizedByteArray, allocRet)+import Data.Proxy (Proxy (Proxy))++import qualified Libsodium as Na+++-- | Hash returned by 'sha256'.+--+-- This type is parametrised by the actual data type that contains+-- bytes. This can be, for example, a @ByteString@.+type HashSha256 a = SizedByteArray Na.CRYPTO_HASH_SHA256_BYTES a++-- | Hash a message using SHA-256.+sha256+ :: ( ByteArrayAccess pt+ , ByteArray hashBytes+ )+ => pt -- ^ Message to hash+ -> IO (HashSha256 hashBytes)+sha256 msg = do+ (_ret, ct) <-+ allocRet (Proxy :: Proxy Na.CRYPTO_HASH_SHA256_BYTES) $ \hashPtr ->+ withByteArray msg $ \msgPtr -> do+ Na.crypto_hash_sha256 hashPtr+ msgPtr (fromIntegral $ length msg)+ -- _ret can be only 0, so we don’t check it+ pure ct+++-- | Hash returned by 'sha512'.+--+-- This type is parametrised by the actual data type that contains+-- bytes. This can be, for example, a @ByteString@.+type HashSha512 a = SizedByteArray Na.CRYPTO_HASH_SHA512_BYTES a++-- | Hash a message using SHA-512.+sha512+ :: ( ByteArrayAccess pt+ , ByteArray hashBytes+ )+ => pt -- ^ Message to hash+ -> IO (HashSha512 hashBytes)+sha512 msg = do+ (_ret, ct) <-+ allocRet (Proxy :: Proxy Na.CRYPTO_HASH_SHA512_BYTES) $ \hashPtr ->+ withByteArray msg $ \msgPtr -> do+ Na.crypto_hash_sha512 hashPtr+ msgPtr (fromIntegral $ length msg)+ -- _ret can be only 0, so we don’t check it+ pure ct
+ lib/NaCl/Scalarmult.hs view
@@ -0,0 +1,102 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | Scalar multiplication in a group.+--+-- This is @crypto_scalarmult_*@ from NaCl.+--+-- Note that this primitive is designed to only make the /Computational Diffie–Hellman/+-- problem hard. It makes no promises about other assumptions, therefore it is+-- the user’s responsibility to hash the output if required for the security+-- of the specific application.+module NaCl.Scalarmult+ ( Point (..)+ , toPoint+ , Scalar (..)+ , toScalar++ , mult+ , multBase+ ) where++import Data.ByteArray (ByteArray, ByteArrayAccess, withByteArray)+import Data.ByteArray.Sized (ByteArrayN, SizedByteArray, allocRet, sizedByteArray)+import Data.Proxy (Proxy (Proxy))+import System.IO.Unsafe (unsafePerformIO)++import qualified Libsodium as Na+++-- | Point in the group.+--+-- This type is parametrised by the actual data type that contains+-- bytes. This can be, for example, a @ByteString@.+newtype Point a = Point (SizedByteArray Na.CRYPTO_SCALARMULT_BYTES a)+ deriving+ ( ByteArrayAccess, ByteArrayN Na.CRYPTO_SCALARMULT_BYTES+ , Eq, Ord, Show+ )++-- | Convert bytes to a group point.+toPoint :: ByteArrayAccess bytes => bytes -> Maybe (Point bytes)+toPoint = fmap Point . sizedByteArray++-- | Scalar that can be used for group multiplication.+--+-- This type is parametrised by the actual data type that contains+-- bytes. This can be, for example, a @ByteString@.+newtype Scalar a = Scalar (SizedByteArray Na.CRYPTO_SCALARMULT_SCALARBYTES a)+ deriving+ ( ByteArrayAccess, ByteArrayN Na.CRYPTO_SCALARMULT_SCALARBYTES+ , Eq, Ord, Show+ )++-- | Convert bytes to a scalar.+toScalar :: ByteArrayAccess bytes => bytes -> Maybe (Scalar bytes)+toScalar = fmap Scalar . sizedByteArray++-- | Multiply a group point by an integer.+--+-- Note that this function is slightly different from the corresponding function+-- in NaCl. Namely, unlike @crypto_scalarmult@ in NaCl, this one will return+-- @Nothing@ if:+--+-- * either the group point has a small order (1, 2, 4, or 8)+-- * or the result of the multiplication is the identity point.+--+-- This is how it is implemented in libsodium.+mult+ :: forall outBytes pointBytes scalarBytes.+ ( ByteArrayAccess pointBytes+ , ByteArrayAccess scalarBytes+ , ByteArray outBytes+ )+ => Point pointBytes -- ^ Group point.+ -> Scalar scalarBytes -- ^ Scalar.+ -> Maybe (Point outBytes)+mult point scalar = unsafePerformIO $ do+ (ret, out) <-+ allocRet (Proxy @Na.CRYPTO_SCALARMULT_BYTES) $ \outPtr ->+ withByteArray point $ \pointPtr ->+ withByteArray scalar $ \scalarPtr ->+ Na.crypto_scalarmult outPtr scalarPtr pointPtr+ if ret == 0+ then pure $ Just out+ else pure Nothing++-- | Multiply the standard group point by an integer.+multBase+ :: forall outBytes scalarBytes.+ ( ByteArrayAccess scalarBytes+ , ByteArray outBytes+ )+ => Scalar scalarBytes -- ^ Scalar.+ -> Point outBytes+multBase scalar = unsafePerformIO $ do+ (_ret, out) <-+ allocRet (Proxy @Na.CRYPTO_SCALARMULT_BYTES) $ \outPtr ->+ withByteArray scalar $ \scalarPtr ->+ Na.crypto_scalarmult_base outPtr scalarPtr+ -- _ret can be only 0, so we don’t check it+ pure out
+ lib/NaCl/Secretbox.hs view
@@ -0,0 +1,112 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | Symmetric authenticated encryption.+--+-- It is best to import this module qualified:+--+-- @+-- import qualified NaCl.Secretbox as Secretbox+--+-- encrypted = Secretbox.'create' key nonce message+-- decrypted = Secretbox.'open' key nonce encrypted+-- @+--+-- This is @crypto_secretbox_*@ from NaCl.+module NaCl.Secretbox+ ( Key+ , toKey++ , Nonce+ , toNonce++ , create+ , open+ ) where++import Data.ByteArray (ByteArray, ByteArrayAccess)+import System.IO.Unsafe (unsafePerformIO)++import NaCl.Secretbox.Internal (Key, Nonce, toKey, toNonce)++import qualified NaCl.Secretbox.Internal as I+++-- | Encrypt a message.+--+-- @+-- encrypted = Secretbox.create key nonce message+-- @+--+-- * @key@ is the secret key used for encryption. There are two typical ways+-- of creating it:+--+-- 1. /Derive from a password/. If you want to protect a message with a password,+-- you must use a+-- <https://en.wikipedia.org/wiki/Key_derivation_function key derivation function>+-- to turn this password into an encryption key.+--+-- 2. /Generate a random one/. This can be useful in certain situations when+-- you want to have an intermediate key that you will encrypt and share+-- later.+--+-- The @Crypto.Key@ module in+-- <https://hackage.haskell.org/package/crypto-sodium crypto-sodium>+-- has functions to help in either case.+--+-- * @nonce@ is an extra noise that is required for security.+-- There are two standard ways of getting it:+--+-- 1. /Use a counter/. In this case you keep a counter of encrypted messages,+-- which means that the nonce will be new for each new message.+--+-- 2. /Random/. You generate a random nonce every time you encrypt a message.+-- Since the nonce is large enough, the chances of you using the same+-- nonce twice are negligible. For useful helpers, see @Crypto.Random@,+-- in <https://hackage.haskell.org/package/crypto-sodium crypto-sodium>.+--+-- In either case, you need to be able to provide the same nonce when decrypting,+-- so you should be able to recover it by computation (e.g. in the case of+-- a counter) or you should store it alongside the encrypted data. The nonce+-- is not secret, so it is perfectly ok to store it in plaintext.+--+-- * @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 key nonce msg =+ -- This IO is safe, because it is pure.+ unsafePerformIO $ I.create key nonce msg+++-- | Decrypt a message.+--+-- @+-- decrypted = Secretbox.open 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.+open+ :: ( ByteArrayAccess keyBytes, ByteArrayAccess nonceBytes+ , ByteArray ptBytes, ByteArrayAccess ctBytes+ )+ => Key keyBytes -- ^ Secret key+ -> Nonce nonceBytes -- ^ Nonce+ -> ctBytes -- ^ Encrypted message (cyphertext)+ -> Maybe ptBytes+open key nonce ct =+ -- This IO is safe, because it is pure.+ unsafePerformIO $ I.open key nonce ct
+ lib/NaCl/Secretbox/Internal.hs view
@@ -0,0 +1,109 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | Internals of @crypto_secretbox@.+module NaCl.Secretbox.Internal+ ( Key+ , toKey++ , Nonce+ , toNonce++ , create+ , open+ ) where++import Prelude hiding (length)++import Data.ByteArray (ByteArray, ByteArrayAccess, allocRet, length, withByteArray)+import Data.ByteArray.Sized (SizedByteArray, sizedByteArray)++import qualified Libsodium as Na+++-- | Encryption key that can be used for Secretbox.+--+-- 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_SECRETBOX_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 Secretbox.+toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)+toKey = sizedByteArray+++-- | Nonce that can be used for Secretbox.+--+-- This type is parametrised by the actual data type that contains+-- bytes. This can be, for example, a @ByteString@.+type Nonce a = SizedByteArray Na.CRYPTO_SECRETBOX_NONCEBYTES a++-- | Make a 'Nonce' 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 nonce with a Secretbox.+toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)+toNonce = sizedByteArray+++-- | Encrypt a message.+create+ :: ( ByteArrayAccess key, ByteArrayAccess nonce+ , ByteArrayAccess pt, ByteArray ct+ )+ => Key key -- ^ Secret key+ -> Nonce nonce -- ^ Nonce+ -> pt -- ^ Plaintext message+ -> IO ct+create key nonce msg = do+ (_ret, ct) <-+ allocRet clen $ \ctPtr ->+ withByteArray key $ \keyPtr ->+ withByteArray nonce $ \noncePtr ->+ withByteArray msg $ \msgPtr -> do+ -- TODO: Maybe, reimplement this without _easy, to stay closer+ -- to the original NaCl.+ Na.crypto_secretbox_easy ctPtr+ msgPtr (fromIntegral $ length msg)+ noncePtr+ keyPtr+ -- _ret can be only 0, so we don’t check it+ pure ct+ where+ clen :: Int+ clen = fromIntegral Na.crypto_secretbox_macbytes + length msg+++-- | Decrypt a message.+open+ :: ( ByteArrayAccess key, ByteArrayAccess nonce+ , ByteArray pt, ByteArrayAccess ct+ )+ => Key key -- ^ Secret key+ -> Nonce nonce -- ^ Nonce+ -> ct -- ^ Cyphertext+ -> IO (Maybe pt)+open key nonce ct = do+ (ret, msg) <-+ allocRet mlen $ \msgPtr ->+ withByteArray key $ \keyPtr ->+ withByteArray nonce $ \noncePtr ->+ withByteArray ct $ \ctPtr -> do+ -- TODO: Maybe, reimplement this without _easy, to stay closer+ -- to the original NaCl.+ Na.crypto_secretbox_open_easy msgPtr+ ctPtr (fromIntegral $ length ct)+ noncePtr+ keyPtr+ if ret == 0 then+ pure $ Just msg+ else+ pure Nothing+ where+ mlen :: Int+ mlen = length ct - fromIntegral Na.crypto_secretbox_macbytes
+ lib/NaCl/Sign.hs view
@@ -0,0 +1,83 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | Public-key signatures.+--+-- It is best to import this module qualified:+--+-- @+-- import qualified NaCl.Sign as Sign+--+-- signed = Sign.'create' sk message+-- verified = Sign.'open' pk signed+-- @+--+-- This is @crypto_sign_*@ from NaCl.+module NaCl.Sign+ ( PublicKey+ , toPublicKey+ , SecretKey+ , toSecretKey+ , keypair++ , create+ , open+ ) where++import Data.ByteArray (ByteArray, ByteArrayAccess)+import System.IO.Unsafe (unsafePerformIO)++import NaCl.Sign.Internal (PublicKey, SecretKey, keypair, toPublicKey, toSecretKey)++import qualified NaCl.Sign.Internal as I+++-- | Sign a message.+--+-- @+-- signed = Sign.create sk message+-- @+--+-- * @sk@ is the signer’s secret key, used for authentication.+--+-- This is generated using 'keypair' and the public part of the key+-- needs to be given to the verifying party in advance.+--+-- * @message@ is the data you are signing.+--+-- This function will copy the message to a new location+-- and add a signature, so that 'open' will refuce to verify it.+create+ :: ( ByteArrayAccess skBytes+ , ByteArrayAccess ptBytes, ByteArray ctBytes+ )+ => SecretKey skBytes -- ^ Signer’s secret key+ -> ptBytes -- ^ Message to sign+ -> ctBytes+create sk msg =+ -- This IO is safe, because it is pure.+ unsafePerformIO $ I.create sk msg+++-- | Verify a signature.+--+-- @+-- verified = Sign.open pk signed+-- @+--+-- * @pk@ is the signer’s public key.+-- * @signed@ is the output of 'create'.+--+-- This function will return @Nothing@ if the signature on the message+-- is invalid.+open+ :: ( ByteArrayAccess pkBytes+ , ByteArray ptBytes, ByteArrayAccess ctBytes+ )+ => PublicKey pkBytes -- ^ Signer’s public key+ -> ctBytes -- ^ Signed message+ -> Maybe ptBytes+open pk ct =+ -- This IO is safe, because it is pure.+ unsafePerformIO $ I.open pk ct
+ lib/NaCl/Sign/Internal.hs view
@@ -0,0 +1,116 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | Internals of @crypto_sign@.+module NaCl.Sign.Internal+ ( SecretKey+ , toSecretKey+ , PublicKey+ , toPublicKey+ , keypair++ , create+ , open+ ) where++import Prelude hiding (length)++import Data.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes, allocRet, length, withByteArray)+import Data.ByteArray.Sized (SizedByteArray, sizedByteArray)+import Data.ByteString (ByteString)+import Data.Functor (void)+import Data.Proxy (Proxy (Proxy))+import Foreign.Ptr (nullPtr)++import qualified Data.ByteArray.Sized as Sized (alloc, allocRet)+import qualified Libsodium as Na+++-- | Secret key that can be used for creating a signature.+--+-- 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 SecretKey a = SizedByteArray Na.CRYPTO_SIGN_SECRETKEYBYTES a++-- | Convert bytes to a secret key.+toSecretKey :: ByteArrayAccess bytes => bytes -> Maybe (SecretKey bytes)+toSecretKey = sizedByteArray++-- | Public key that can be used for verifyiing a signature.+--+-- This type is parametrised by the actual data type that contains+-- bytes. This can be, for example, a @ByteString@.+type PublicKey a = SizedByteArray Na.CRYPTO_SIGN_PUBLICKEYBYTES a++-- | Convert bytes to a public key.+toPublicKey :: ByteArrayAccess bytes => bytes -> Maybe (PublicKey bytes)+toPublicKey = sizedByteArray++-- | Generate a new 'SecretKey' together with its 'PublicKey'.+--+-- Note: this function is not thread-safe (since the underlying+-- C function is not thread-safe both in Sodium and in NaCl)!+-- Either make sure there are no concurrent calls or see+-- @Crypto.Init@ in+-- <https://hackage.haskell.org/package/crypto-sodium crypto-sodium>+-- to learn how to make this function thread-safe.+keypair :: IO (PublicKey ByteString, SecretKey ScrubbedBytes)+keypair = do+ (pk, sk) <-+ Sized.allocRet Proxy $ \skPtr ->+ Sized.alloc $ \pkPtr ->+ -- always returns 0, so we don’t check it+ void $ Na.crypto_sign_keypair pkPtr skPtr+ pure (pk, sk)+++-- | Sign a message.+create+ :: ( ByteArrayAccess skBytes+ , ByteArrayAccess pt, ByteArray ct+ )+ => SecretKey skBytes -- ^ Signer’s secret key+ -> pt -- ^ Message to sign+ -> IO ct+create sk msg = do+ (_ret, ct) <-+ allocRet clen $ \ctPtr ->+ withByteArray sk $ \skPtr ->+ withByteArray msg $ \msgPtr -> do+ Na.crypto_sign ctPtr nullPtr+ msgPtr (fromIntegral $ length msg)+ skPtr+ -- _ret can be only 0, so we don’t check it+ -- TODO: Actually, it looks like this function can fail and return+ -- a -1, even though this is not documented :/.+ pure ct+ where+ clen :: Int+ clen = fromIntegral Na.crypto_sign_bytes + length msg+++-- | Verify the signature of a signed message.+open+ :: ( ByteArrayAccess pkBytes+ , ByteArray pt, ByteArrayAccess ct+ )+ => PublicKey pkBytes -- ^ Signer’s public key+ -> ct -- ^ Signed message+ -> IO (Maybe pt)+open pk ct = do+ (ret, msg) <-+ allocRet mlen $ \msgPtr ->+ withByteArray pk $ \pkPtr ->+ withByteArray ct $ \ctPtr -> do+ Na.crypto_sign_open msgPtr nullPtr+ ctPtr (fromIntegral $ length ct)+ pkPtr+ if ret == 0 then+ pure $ Just msg+ else+ pure Nothing+ where+ mlen :: Int+ mlen = length ct - fromIntegral Na.crypto_sign_bytes
+ lib/NaCl/Stream.hs view
@@ -0,0 +1,68 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}++-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- ! This module intentionally does not contain extensive documentation+-- ! as a measure for discouraging its use.++-- | Unauthenticated streaming encryption.+--+-- __Note:__ Unauthenticated encryption is __insecure__ in general.+-- Only use the functions from this modules if you know exactly what you are doing.+-- We only provide this module for compatibility with NaCl.+-- @+--+-- This is @crypto_box_*@ from NaCl.+module NaCl.Stream+ ( Key+ , toKey++ , Nonce+ , toNonce++ , MaxStreamSize+ , generate++ , xor+ ) where++import Data.ByteArray (ByteArray, ByteArrayAccess)+import Data.ByteArray.Sized (ByteArrayN)+import GHC.TypeLits (type (<=))+import System.IO.Unsafe (unsafePerformIO)++import NaCl.Stream.Internal (Nonce, Key, MaxStreamSize, toNonce, toKey)++import qualified NaCl.Stream.Internal as I+++-- | Generate a stream of pseudo-random bytes.+generate+ :: forall n key nonce ct.+ ( ByteArrayAccess key, ByteArrayAccess nonce+ , ByteArrayN n ct+ , n <= MaxStreamSize+ )+ => Key key -- ^ Secret key+ -> Nonce nonce -- ^ Nonce+ -> ct+generate key nonce =+ -- This IO is safe, because it is pure.+ unsafePerformIO $ I.generate key nonce+++-- | Encrypt/decrypt a message.+xor+ :: ( ByteArrayAccess key, ByteArrayAccess nonce+ , ByteArrayAccess pt, ByteArray ct+ )+ => Key key -- ^ Secret key+ -> Nonce nonce -- ^ Nonce+ -> pt -- ^ Input (plain/cipher) text+ -> ct+xor key nonce msg =+ -- This IO is safe, because it is pure.+ unsafePerformIO $ I.xor key nonce msg
+ lib/NaCl/Stream/Internal.hs view
@@ -0,0 +1,112 @@+{-# OPTIONS_GHC -Wno-redundant-constraints #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}++-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++-- | Internals of @crypto_stream@.+module NaCl.Stream.Internal+ ( Key+ , toKey++ , Nonce+ , toNonce++ , MaxStreamSize+ , generate++ , xor+ ) where++import Prelude hiding (length)++import Data.ByteArray (ByteArray, ByteArrayAccess, allocRet, length, withByteArray)+import Data.ByteArray.Sized (ByteArrayN, SizedByteArray, sizedByteArray)+import Data.Proxy (Proxy (Proxy))+import GHC.TypeLits (type (<=), natVal)++import qualified Data.ByteArray.Sized as Sized (allocRet)+import qualified Libsodium as Na+++-- | Encryption key that can be used for Stream.+--+-- 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_STREAM_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 Stream.+toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)+toKey = sizedByteArray+++-- | Nonce that can be used for Stream.+--+-- This type is parametrised by the actual data type that contains+-- bytes. This can be, for example, a @ByteString@.+type Nonce a = SizedByteArray Na.CRYPTO_STREAM_NONCEBYTES a++-- | Make a 'Nonce' 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 nonce with a Stream.+toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)+toNonce = sizedByteArray+++-- | The maximum size of the stream produced by 'generate'.+type MaxStreamSize = 18446744073709551615 -- = 2^64 - 1 (internal 64-bit counter)++-- | Generate a stream of pseudo-random bytes.+generate+ :: forall key nonce n ct.+ ( ByteArrayAccess key, ByteArrayAccess nonce+ , ByteArrayN n ct+ , n <= MaxStreamSize+ )+ => Key key -- ^ Secret key+ -> Nonce nonce -- ^ Nonce+ -> IO ct+generate key nonce = do+ (_ret, ct) <-+ Sized.allocRet (Proxy :: Proxy n) $ \ctPtr ->+ withByteArray key $ \keyPtr ->+ withByteArray nonce $ \noncePtr ->+ Na.crypto_stream ctPtr+ (fromIntegral $ natVal (Proxy :: Proxy n))+ noncePtr+ keyPtr+ -- _ret can be only 0, so we don’t check it+ pure ct+++-- | Encrypt/decrypt a message.+xor+ :: ( ByteArrayAccess key, ByteArrayAccess nonce+ , ByteArrayAccess pt, ByteArray ct+ )+ => Key key -- ^ Secret key+ -> Nonce nonce -- ^ Nonce+ -> pt -- ^ Input (plain/cipher) text+ -> IO ct+xor key nonce msg = do+ (_ret, ct) <-+ allocRet clen $ \ctPtr ->+ withByteArray key $ \keyPtr ->+ withByteArray nonce $ \noncePtr ->+ withByteArray msg $ \msgPtr -> do+ Na.crypto_stream_xor ctPtr+ msgPtr (fromIntegral $ length msg)+ noncePtr+ keyPtr+ -- _ret can be only 0, so we don’t check it+ pure ct+ where+ clen :: Int+ clen = length msg
+ lib/NaCl/Verify.hs view
@@ -0,0 +1,69 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++{-# LANGUAGE TypeFamilies #-}++-- | String comparison.+--+-- This is @crypto_verify_*@ from NaCl.+--+-- Unlike NaCl, which provides multiple functions, this module exports only+-- one function – 'eq'. The function will automatically pick the correct+-- implementation based on type-level sizes of its inputs.+module NaCl.Verify+ ( eq+ , NaClComparable+ ) where++import Data.ByteArray (ByteArrayAccess, withByteArray)+import Data.ByteArray.Sized (SizedByteArray)+import Data.Proxy (Proxy (Proxy))+import Foreign.C.Types (CInt, CUChar)+import Foreign.Ptr (Ptr)+import GHC.TypeLits (KnownNat, Nat)+import System.IO.Unsafe (unsafeDupablePerformIO)++import qualified Libsodium as Na+++-- | Class of bytestring lengths that can be verified by NaCl.+--+-- This is a private class, we do not export it so that it is closed.+-- External users can use the @NaClComparable@ constraint on their functions,+-- but they can’t add instances.+class KnownNat n => CryptoVerify (n :: Nat) where+ crypto_verify_n :: Proxy n -> Ptr CUChar -> Ptr CUChar -> IO CInt++-- | Class of bytestring lengths that can be compared in constant-time+-- by NaCl.+class CryptoVerify n => NaClComparable n where++instance CryptoVerify 16 where+ crypto_verify_n _proxy = Na.crypto_verify_16+instance NaClComparable 16 where++instance CryptoVerify 32 where+ crypto_verify_n _proxy = Na.crypto_verify_32+instance NaClComparable 32 where+++-- | Constant-time comparison of sequences of bytes.+--+-- Unlike regular comparison, this function will always read both+-- sequences until the end rather than exit as soon as it finds+-- differing bytes. This makes it suitable for comparing secret data.+--+-- It only works with inputs of size 16 or 32.+eq+ :: forall n xBytes yBytes.+ ( NaClComparable n+ , ByteArrayAccess xBytes, ByteArrayAccess yBytes+ )+ => SizedByteArray n xBytes -> SizedByteArray n yBytes -> Bool+eq x y = unsafeDupablePerformIO $ do+ ret <-+ withByteArray x $ \xPtr ->+ withByteArray y $ \yPtr ->+ crypto_verify_n (Proxy :: Proxy n) xPtr yPtr+ pure $ ret == 0
− test/Test/Crypto/Box.hs
@@ -1,113 +0,0 @@--- SPDX-FileCopyrightText: 2020 Serokell------ SPDX-License-Identifier: MPL-2.0--{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}--module Test.Crypto.Box where--import Hedgehog (Property, forAll, property, tripping)-import Hedgehog.Internal.Property (forAllT)-import Test.HUnit ((@?=), Assertion)--import Data.ByteArray (ScrubbedBytes, convert)-import Data.ByteString (ByteString)-import Control.Monad.IO.Class (liftIO)--import qualified Data.ByteString as BS-import qualified Libsodium as Na--import qualified Hedgehog.Gen as G-import qualified Hedgehog.Range as R--import qualified Crypto.Box as Box---nonceSize :: R.Range Int-nonceSize = R.singleton $ fromIntegral Na.crypto_box_noncebytes---hprop_encode_decode :: Property-hprop_encode_decode = property $ do- (pkS, skS) <- forAllT $ liftIO $ Box.keypair- (pkR, skR) <- forAllT $ liftIO $ Box.keypair- nonceBytes <- forAll $ G.bytes nonceSize- let Just nonce = Box.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 = Box.create pkR skS nonce msg :: ByteString- decodeBs skR pkS nonce ct = Box.open skR pkS nonce ct :: Maybe ByteString----- Test vector from--- https://github.com/jedisct1/libsodium/blob/f911b56650b680ecfc5d32b11b090849fc2b5f92/test/default/box.c--exampleSk :: ScrubbedBytes-exampleSk = convert $ BS.pack $- [ 0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1- , 0x72, 0x51, 0xb2, 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0- , 0x99, 0x2a, 0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a- ]--examplePk :: ByteString-examplePk = BS.pack $- [ 0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, 0x5b, 0x61- , 0xc2, 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78- , 0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f- ]--exampleNonce :: ByteString-exampleNonce = BS.pack $- [ 0x69, 0x69, 0x6e, 0xe9, 0x55, 0xb6- , 0x2b, 0x73, 0xcd, 0x62, 0xbd, 0xa8- , 0x75, 0xfc, 0x73, 0xd6, 0x82, 0x19- , 0xe0, 0x03, 0x6b, 0x7a, 0x0b, 0x37- ]--exampleMsg :: ByteString-exampleMsg = BS.pack $- [ 0xbe, 0x07, 0x5f, 0xc5- , 0x3c, 0x81, 0xf2, 0xd5, 0xcf, 0x14, 0x13, 0x16, 0xeb, 0xeb, 0x0c, 0x7b- , 0x52, 0x28, 0xc5, 0x2a, 0x4c, 0x62, 0xcb, 0xd4, 0x4b, 0x66, 0x84, 0x9b- , 0x64, 0x24, 0x4f, 0xfc, 0xe5, 0xec, 0xba, 0xaf, 0x33, 0xbd, 0x75, 0x1a- , 0x1a, 0xc7, 0x28, 0xd4, 0x5e, 0x6c, 0x61, 0x29, 0x6c, 0xdc, 0x3c, 0x01- , 0x23, 0x35, 0x61, 0xf4, 0x1d, 0xb6, 0x6c, 0xce, 0x31, 0x4a, 0xdb, 0x31- , 0x0e, 0x3b, 0xe8, 0x25, 0x0c, 0x46, 0xf0, 0x6d, 0xce, 0xea, 0x3a, 0x7f- , 0xa1, 0x34, 0x80, 0x57, 0xe2, 0xf6, 0x55, 0x6a, 0xd6, 0xb1, 0x31, 0x8a- , 0x02, 0x4a, 0x83, 0x8f, 0x21, 0xaf, 0x1f, 0xde, 0x04, 0x89, 0x77, 0xeb- , 0x48, 0xf5, 0x9f, 0xfd, 0x49, 0x24, 0xca, 0x1c, 0x60, 0x90, 0x2e, 0x52- , 0xf0, 0xa0, 0x89, 0xbc, 0x76, 0x89, 0x70, 0x40, 0xe0, 0x82, 0xf9, 0x37- , 0x76, 0x38, 0x48, 0x64, 0x5e, 0x07, 0x05- ]--exampleCt :: ByteString-exampleCt = BS.pack $- [ 0xf3, 0xff, 0xc7, 0x70, 0x3f, 0x94, 0x00, 0xe5- , 0x2a, 0x7d, 0xfb, 0x4b, 0x3d, 0x33, 0x05, 0xd9- , 0x8e, 0x99, 0x3b, 0x9f, 0x48, 0x68, 0x12, 0x73- , 0xc2, 0x96, 0x50, 0xba, 0x32, 0xfc, 0x76, 0xce- , 0x48, 0x33, 0x2e, 0xa7, 0x16, 0x4d, 0x96, 0xa4- , 0x47, 0x6f, 0xb8, 0xc5, 0x31, 0xa1, 0x18, 0x6a- , 0xc0, 0xdf, 0xc1, 0x7c, 0x98, 0xdc, 0xe8, 0x7b- , 0x4d, 0xa7, 0xf0, 0x11, 0xec, 0x48, 0xc9, 0x72- , 0x71, 0xd2, 0xc2, 0x0f, 0x9b, 0x92, 0x8f, 0xe2- , 0x27, 0x0d, 0x6f, 0xb8, 0x63, 0xd5, 0x17, 0x38- , 0xb4, 0x8e, 0xee, 0xe3, 0x14, 0xa7, 0xcc, 0x8a- , 0xb9, 0x32, 0x16, 0x45, 0x48, 0xe5, 0x26, 0xae- , 0x90, 0x22, 0x43, 0x68, 0x51, 0x7a, 0xcf, 0xea- , 0xbd, 0x6b, 0xb3, 0x73, 0x2b, 0xc0, 0xe9, 0xda- , 0x99, 0x83, 0x2b, 0x61, 0xca, 0x01, 0xb6, 0xde- , 0x56, 0x24, 0x4a, 0x9e, 0x88, 0xd5, 0xf9, 0xb3- , 0x79, 0x73, 0xf6, 0x22, 0xa4, 0x3d, 0x14, 0xa6- , 0x59, 0x9b, 0x1f, 0x65, 0x4c, 0xb4, 0x5a, 0x74- , 0xe3, 0x55, 0xa5- ]--unit_example_create :: Assertion-unit_example_create = do- let Just sk = Box.toSecretKey exampleSk- let Just pk = Box.toPublicKey examplePk- let Just nonce = Box.toNonce exampleNonce- Box.create pk sk nonce exampleMsg @?= exampleCt
− test/Test/Crypto/Secretbox.hs
@@ -1,113 +0,0 @@--- SPDX-FileCopyrightText: 2020 Serokell------ SPDX-License-Identifier: MPL-2.0--{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}--module Test.Crypto.Secretbox where--import Hedgehog (Property, forAll, property, tripping)-import Test.HUnit ((@?=), Assertion)--import Data.ByteString (ByteString)--import qualified Data.ByteString as BS-import qualified Libsodium as Na--import qualified Hedgehog.Gen as G-import qualified Hedgehog.Range as R--import qualified Crypto.Secretbox as Secretbox---keySize :: R.Range Int-keySize = R.singleton $ fromIntegral Na.crypto_secretbox_keybytes--nonceSize :: R.Range Int-nonceSize = R.singleton $ fromIntegral Na.crypto_secretbox_noncebytes---hprop_encode_decode :: Property-hprop_encode_decode = property $ do- keyBytes <- forAll $ G.bytes keySize- let Just key = Secretbox.toKey keyBytes- nonceBytes <- forAll $ G.bytes nonceSize- let Just nonce = Secretbox.toNonce nonceBytes- 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 vector from--- https://github.com/jedisct1/libsodium/blob/f911b56650b680ecfc5d32b11b090849fc2b5f92/test/default/secretbox.c--exampleKey :: ByteString-exampleKey = BS.pack $- [ 0x1b, 0x27, 0x55, 0x64, 0x73, 0xe9, 0x85- , 0xd4, 0x62, 0xcd, 0x51, 0x19, 0x7a, 0x9a- , 0x46, 0xc7, 0x60, 0x09, 0x54, 0x9e, 0xac- , 0x64, 0x74, 0xf2, 0x06, 0xc4, 0xee, 0x08- , 0x44, 0xf6, 0x83, 0x89- ]--exampleNonce :: ByteString-exampleNonce = BS.pack $- [ 0x69, 0x69, 0x6e, 0xe9, 0x55, 0xb6- , 0x2b, 0x73, 0xcd, 0x62, 0xbd, 0xa8- , 0x75, 0xfc, 0x73, 0xd6, 0x82, 0x19- , 0xe0, 0x03, 0x6b, 0x7a, 0x0b, 0x37- ]--exampleMsg :: ByteString-exampleMsg = BS.pack $- [ 0xbe, 0x07, 0x5f, 0xc5- , 0x3c, 0x81, 0xf2, 0xd5, 0xcf, 0x14, 0x13, 0x16, 0xeb, 0xeb, 0x0c, 0x7b- , 0x52, 0x28, 0xc5, 0x2a, 0x4c, 0x62, 0xcb, 0xd4, 0x4b, 0x66, 0x84, 0x9b- , 0x64, 0x24, 0x4f, 0xfc, 0xe5, 0xec, 0xba, 0xaf, 0x33, 0xbd, 0x75, 0x1a- , 0x1a, 0xc7, 0x28, 0xd4, 0x5e, 0x6c, 0x61, 0x29, 0x6c, 0xdc, 0x3c, 0x01- , 0x23, 0x35, 0x61, 0xf4, 0x1d, 0xb6, 0x6c, 0xce, 0x31, 0x4a, 0xdb, 0x31- , 0x0e, 0x3b, 0xe8, 0x25, 0x0c, 0x46, 0xf0, 0x6d, 0xce, 0xea, 0x3a, 0x7f- , 0xa1, 0x34, 0x80, 0x57, 0xe2, 0xf6, 0x55, 0x6a, 0xd6, 0xb1, 0x31, 0x8a- , 0x02, 0x4a, 0x83, 0x8f, 0x21, 0xaf, 0x1f, 0xde, 0x04, 0x89, 0x77, 0xeb- , 0x48, 0xf5, 0x9f, 0xfd, 0x49, 0x24, 0xca, 0x1c, 0x60, 0x90, 0x2e, 0x52- , 0xf0, 0xa0, 0x89, 0xbc, 0x76, 0x89, 0x70, 0x40, 0xe0, 0x82, 0xf9, 0x37- , 0x76, 0x38, 0x48, 0x64, 0x5e, 0x07, 0x05- ]--exampleCt :: ByteString-exampleCt = BS.pack $- [ 0xf3, 0xff, 0xc7, 0x70, 0x3f, 0x94, 0x00, 0xe5- , 0x2a, 0x7d, 0xfb, 0x4b, 0x3d, 0x33, 0x05, 0xd9- , 0x8e, 0x99, 0x3b, 0x9f, 0x48, 0x68, 0x12, 0x73- , 0xc2, 0x96, 0x50, 0xba, 0x32, 0xfc, 0x76, 0xce- , 0x48, 0x33, 0x2e, 0xa7, 0x16, 0x4d, 0x96, 0xa4- , 0x47, 0x6f, 0xb8, 0xc5, 0x31, 0xa1, 0x18, 0x6a- , 0xc0, 0xdf, 0xc1, 0x7c, 0x98, 0xdc, 0xe8, 0x7b- , 0x4d, 0xa7, 0xf0, 0x11, 0xec, 0x48, 0xc9, 0x72- , 0x71, 0xd2, 0xc2, 0x0f, 0x9b, 0x92, 0x8f, 0xe2- , 0x27, 0x0d, 0x6f, 0xb8, 0x63, 0xd5, 0x17, 0x38- , 0xb4, 0x8e, 0xee, 0xe3, 0x14, 0xa7, 0xcc, 0x8a- , 0xb9, 0x32, 0x16, 0x45, 0x48, 0xe5, 0x26, 0xae- , 0x90, 0x22, 0x43, 0x68, 0x51, 0x7a, 0xcf, 0xea- , 0xbd, 0x6b, 0xb3, 0x73, 0x2b, 0xc0, 0xe9, 0xda- , 0x99, 0x83, 0x2b, 0x61, 0xca, 0x01, 0xb6, 0xde- , 0x56, 0x24, 0x4a, 0x9e, 0x88, 0xd5, 0xf9, 0xb3- , 0x79, 0x73, 0xf6, 0x22, 0xa4, 0x3d, 0x14, 0xa6- , 0x59, 0x9b, 0x1f, 0x65, 0x4c, 0xb4, 0x5a, 0x74- , 0xe3, 0x55, 0xa5- ]--unit_example_create :: Assertion-unit_example_create = do- let Just key = Secretbox.toKey exampleKey- let Just nonce = Secretbox.toNonce exampleNonce- Secretbox.create key nonce exampleMsg @?= exampleCt--unit_example_open :: Assertion-unit_example_open = do- let Just key = Secretbox.toKey exampleKey- let Just nonce = Secretbox.toNonce exampleNonce- Secretbox.open key nonce exampleCt @?= Just exampleMsg
+ test/Test/NaCl/Auth.hs view
@@ -0,0 +1,63 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}++module Test.NaCl.Auth where++import Hedgehog (Property, assert, forAll, property)+import Test.HUnit ((@?), (@?=), Assertion)++import Data.ByteArray.Sized (unSizedByteArray)+import Data.ByteString (ByteString)++import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as C8+import qualified Libsodium as Na++import qualified Hedgehog.Gen as G+import qualified Hedgehog.Range as R++import qualified NaCl.Auth as Auth+++keySize :: R.Range Int+keySize = R.singleton $ fromIntegral Na.crypto_auth_keybytes+++hprop_verify_of_create :: Property+hprop_verify_of_create = property $ do+ keyBytes <- forAll $ G.bytes keySize+ let Just key = Auth.toKey keyBytes+ msg <- forAll $ G.bytes (R.linear 0 1_000)+ assert $ Auth.verify key msg (Auth.create @ByteString key msg)+++-- Test vector from+-- https://github.com/jedisct1/libsodium/blob/f911b56650b680ecfc5d32b11b090849fc2b5f92/test/default/auth.c++exampleKey :: ByteString+exampleKey = C8.pack $ "Jefe" ++ replicate (32 - 4) '\NUL'++exampleMsg :: ByteString+exampleMsg = C8.pack "what do ya want for nothing?"++exampleAuth :: ByteString+exampleAuth = BS.pack $+ [ 0x16,0x4b,0x7a,0x7b,0xfc,0xf8,0x19,0xe2+ , 0xe3,0x95,0xfb,0xe7,0x3b,0x56,0xe0,0xa3+ , 0x87,0xbd,0x64,0x22,0x2e,0x83,0x1f,0xd6+ , 0x10,0x27,0x0c,0xd7,0xea,0x25,0x05,0x54+ ]++unit_example_create :: Assertion+unit_example_create = do+ let Just key = Auth.toKey exampleKey+ unSizedByteArray (Auth.create key exampleMsg) @?= exampleAuth++unit_example_verify :: Assertion+unit_example_verify = do+ let Just key = Auth.toKey exampleKey+ let Just auth = Auth.toAuthenticator exampleAuth+ Auth.verify key exampleMsg auth @? "Verify ok"
+ test/Test/NaCl/Box.hs view
@@ -0,0 +1,113 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}++module Test.NaCl.Box where++import Hedgehog (Property, forAll, property, tripping)+import Hedgehog.Internal.Property (forAllT)+import Test.HUnit ((@?=), Assertion)++import Data.ByteArray (ScrubbedBytes, convert)+import Data.ByteString (ByteString)+import Control.Monad.IO.Class (liftIO)++import qualified Data.ByteString as BS+import qualified Libsodium as Na++import qualified Hedgehog.Gen as G+import qualified Hedgehog.Range as R++import qualified NaCl.Box as Box+++nonceSize :: R.Range Int+nonceSize = R.singleton $ fromIntegral Na.crypto_box_noncebytes+++hprop_encode_decode :: Property+hprop_encode_decode = property $ do+ (pkS, skS) <- forAllT $ liftIO $ Box.keypair+ (pkR, skR) <- forAllT $ liftIO $ Box.keypair+ nonceBytes <- forAll $ G.bytes nonceSize+ let Just nonce = Box.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 = Box.create pkR skS nonce msg :: ByteString+ decodeBs skR pkS nonce ct = Box.open skR pkS nonce ct :: Maybe ByteString+++-- Test vector from+-- https://github.com/jedisct1/libsodium/blob/f911b56650b680ecfc5d32b11b090849fc2b5f92/test/default/box.c++exampleSk :: ScrubbedBytes+exampleSk = convert $ BS.pack $+ [ 0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1+ , 0x72, 0x51, 0xb2, 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0+ , 0x99, 0x2a, 0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a+ ]++examplePk :: ByteString+examplePk = BS.pack $+ [ 0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, 0x5b, 0x61+ , 0xc2, 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78+ , 0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f+ ]++exampleNonce :: ByteString+exampleNonce = BS.pack $+ [ 0x69, 0x69, 0x6e, 0xe9, 0x55, 0xb6+ , 0x2b, 0x73, 0xcd, 0x62, 0xbd, 0xa8+ , 0x75, 0xfc, 0x73, 0xd6, 0x82, 0x19+ , 0xe0, 0x03, 0x6b, 0x7a, 0x0b, 0x37+ ]++exampleMsg :: ByteString+exampleMsg = BS.pack $+ [ 0xbe, 0x07, 0x5f, 0xc5+ , 0x3c, 0x81, 0xf2, 0xd5, 0xcf, 0x14, 0x13, 0x16, 0xeb, 0xeb, 0x0c, 0x7b+ , 0x52, 0x28, 0xc5, 0x2a, 0x4c, 0x62, 0xcb, 0xd4, 0x4b, 0x66, 0x84, 0x9b+ , 0x64, 0x24, 0x4f, 0xfc, 0xe5, 0xec, 0xba, 0xaf, 0x33, 0xbd, 0x75, 0x1a+ , 0x1a, 0xc7, 0x28, 0xd4, 0x5e, 0x6c, 0x61, 0x29, 0x6c, 0xdc, 0x3c, 0x01+ , 0x23, 0x35, 0x61, 0xf4, 0x1d, 0xb6, 0x6c, 0xce, 0x31, 0x4a, 0xdb, 0x31+ , 0x0e, 0x3b, 0xe8, 0x25, 0x0c, 0x46, 0xf0, 0x6d, 0xce, 0xea, 0x3a, 0x7f+ , 0xa1, 0x34, 0x80, 0x57, 0xe2, 0xf6, 0x55, 0x6a, 0xd6, 0xb1, 0x31, 0x8a+ , 0x02, 0x4a, 0x83, 0x8f, 0x21, 0xaf, 0x1f, 0xde, 0x04, 0x89, 0x77, 0xeb+ , 0x48, 0xf5, 0x9f, 0xfd, 0x49, 0x24, 0xca, 0x1c, 0x60, 0x90, 0x2e, 0x52+ , 0xf0, 0xa0, 0x89, 0xbc, 0x76, 0x89, 0x70, 0x40, 0xe0, 0x82, 0xf9, 0x37+ , 0x76, 0x38, 0x48, 0x64, 0x5e, 0x07, 0x05+ ]++exampleCt :: ByteString+exampleCt = BS.pack $+ [ 0xf3, 0xff, 0xc7, 0x70, 0x3f, 0x94, 0x00, 0xe5+ , 0x2a, 0x7d, 0xfb, 0x4b, 0x3d, 0x33, 0x05, 0xd9+ , 0x8e, 0x99, 0x3b, 0x9f, 0x48, 0x68, 0x12, 0x73+ , 0xc2, 0x96, 0x50, 0xba, 0x32, 0xfc, 0x76, 0xce+ , 0x48, 0x33, 0x2e, 0xa7, 0x16, 0x4d, 0x96, 0xa4+ , 0x47, 0x6f, 0xb8, 0xc5, 0x31, 0xa1, 0x18, 0x6a+ , 0xc0, 0xdf, 0xc1, 0x7c, 0x98, 0xdc, 0xe8, 0x7b+ , 0x4d, 0xa7, 0xf0, 0x11, 0xec, 0x48, 0xc9, 0x72+ , 0x71, 0xd2, 0xc2, 0x0f, 0x9b, 0x92, 0x8f, 0xe2+ , 0x27, 0x0d, 0x6f, 0xb8, 0x63, 0xd5, 0x17, 0x38+ , 0xb4, 0x8e, 0xee, 0xe3, 0x14, 0xa7, 0xcc, 0x8a+ , 0xb9, 0x32, 0x16, 0x45, 0x48, 0xe5, 0x26, 0xae+ , 0x90, 0x22, 0x43, 0x68, 0x51, 0x7a, 0xcf, 0xea+ , 0xbd, 0x6b, 0xb3, 0x73, 0x2b, 0xc0, 0xe9, 0xda+ , 0x99, 0x83, 0x2b, 0x61, 0xca, 0x01, 0xb6, 0xde+ , 0x56, 0x24, 0x4a, 0x9e, 0x88, 0xd5, 0xf9, 0xb3+ , 0x79, 0x73, 0xf6, 0x22, 0xa4, 0x3d, 0x14, 0xa6+ , 0x59, 0x9b, 0x1f, 0x65, 0x4c, 0xb4, 0x5a, 0x74+ , 0xe3, 0x55, 0xa5+ ]++unit_example_create :: Assertion+unit_example_create = do+ let Just sk = Box.toSecretKey exampleSk+ let Just pk = Box.toPublicKey examplePk+ let Just nonce = Box.toNonce exampleNonce+ Box.create pk sk nonce exampleMsg @?= exampleCt
+ test/Test/NaCl/Hash.hs view
@@ -0,0 +1,60 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}++module Test.NaCl.Hash where++import Test.HUnit ((@?=), Assertion)++import Data.ByteArray.Sized (sizedByteArray)+import Data.ByteString (ByteString)+import Data.ByteString.Base16 (decodeBase16)+import Data.Either (fromRight)++import qualified NaCl.Hash as Hash+++-- Test vectors from+-- https://github.com/jedisct1/libsodium/blob/f911b56650b680ecfc5d32b11b090849fc2b5f92/test/default/hash.c++unit_sha512_test1 :: Assertion+unit_sha512_test1 = do+ let+ msg = "testing\n" :: ByteString+ Just hash = sizedByteArray . fromRight (error "impossible") . decodeBase16 $+ "24f950aac7b9ea9b3cb728228a0c82b67c39e96b4b344798870d5daee93e3ae5931baae8c7cacfea4b629452c38026a81d138bc7aad1af3ef7bfd5ec646d6c28"+ Hash.sha512 msg @?= hash++unit_sha512_test2 :: Assertion+unit_sha512_test2 = do+ let+ msg = mconcat @ByteString+ [ "The Conscience of a Hacker is a small essay written January 8, 1986 by a "+ , "computer security hacker who went by the handle of The Mentor, who "+ , "belonged to the 2nd generation of Legion of Doom."+ ]+ Just hash = sizedByteArray . fromRight (error "impossible") . decodeBase16 $+ "a77abe1ccf8f5497e228fbc0acd73a521ededb21b89726684a6ebbc3baa32361aca5a244daa84f24bf19c68baf78e6907625a659b15479eb7bd426fc62aafa73"+ Hash.sha512 msg @?= hash++unit_sha256_test1 :: Assertion+unit_sha256_test1 = do+ let+ msg = "testing\n" :: ByteString+ Just hash = sizedByteArray . fromRight (error "impossible") . decodeBase16 $+ "12a61f4e173fb3a11c05d6471f74728f76231b4a5fcd9667cef3af87a3ae4dc2"+ Hash.sha256 msg @?= hash++unit_sha256_test2 :: Assertion+unit_sha256_test2 = do+ let+ msg = mconcat @ByteString+ [ "The Conscience of a Hacker is a small essay written January 8, 1986 by a "+ , "computer security hacker who went by the handle of The Mentor, who "+ , "belonged to the 2nd generation of Legion of Doom."+ ]+ Just hash = sizedByteArray . fromRight (error "impossible") . decodeBase16 $+ "71cc8123fef8c236e451d3c3ddf1adae9aa6cd9521e7041769d737024900a03a"+ Hash.sha256 msg @?= hash
+ test/Test/NaCl/Scalarmult.hs view
@@ -0,0 +1,97 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}++module Test.NaCl.Scalarmult where++import Hedgehog ((===), Property, discard, failure, forAll, property, success)+import Test.HUnit ((@?=), Assertion)++import Data.ByteString (ByteString)+import Data.ByteString.Base16 (decodeBase16)+import Data.Either (fromRight)++import qualified Data.ByteString as BS++import qualified Hedgehog.Gen as G+import qualified Hedgehog.Range as R++import NaCl.Scalarmult (Scalar, Point, mult, multBase, toScalar, toPoint)+++pointSize :: R.Range Int+pointSize = R.singleton 32++scalarSize :: R.Range Int+scalarSize = R.singleton 32+++-- Test vectors from+-- https://github.com/jedisct1/libsodium/blob/f911b56650b680ecfc5d32b11b090849fc2b5f92/test/default/scalarmult.c++aliceSk :: Scalar ByteString+Just aliceSk = toScalar . BS.pack $+ [ 0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1,+ 0x72, 0x51, 0xb2, 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0,+ 0x99, 0x2a, 0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a+ ]+alicePk :: Point ByteString+alicePk = multBase aliceSk+++bobSk :: Scalar ByteString+Just bobSk = toScalar . BS.pack $+ [ 0x5d, 0xab, 0x08, 0x7e, 0x62, 0x4a, 0x8a, 0x4b, 0x79, 0xe1, 0x7f,+ 0x8b, 0x83, 0x80, 0x0e, 0xe6, 0x6f, 0x3b, 0xb1, 0x29, 0x26, 0x18,+ 0xb6, 0xfd, 0x1c, 0x2f, 0x8b, 0x27, 0xff, 0x88, 0xe0, 0xeb+ ]+bobPk :: Point ByteString+bobPk = multBase bobSk++smallOrderP :: Point ByteString+Just smallOrderP = toPoint . BS.pack $+ [ 0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, 0x16, 0x56, 0xe3,+ 0xfa, 0xf1, 0x9f, 0xc4, 0x6a, 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32,+ 0xb1, 0xfd, 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x00+ ]+++unit_alice_pk :: Assertion+unit_alice_pk = do+ let Just expected = toPoint . fromRight (error "impossible") . decodeBase16 $ "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"+ alicePk @?= expected++unit_bob_pk :: Assertion+unit_bob_pk = do+ let Just expected = toPoint . fromRight (error "impossible") . decodeBase16 $ "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"+ bobPk @?= expected++unit_shared_example :: Assertion+unit_shared_example = do+ let Just aliceShared = bobPk `mult` aliceSk+ let Just bobShared = alicePk `mult` bobSk+ let Just expected = toPoint . fromRight (error "impossible") . decodeBase16 $ "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"+ aliceShared @?= expected+ bobShared @?= expected++hprop_small_order :: Property+hprop_small_order = property $ do+ Just sk <- forAll $ toScalar <$> G.bytes scalarSize+ case mult @ByteString smallOrderP sk of+ Nothing -> success+ Just _ -> failure++hprop_shared :: Property+hprop_shared = property $ do+ Just sk1 <- forAll $ toScalar <$> G.bytes scalarSize+ let pk1 = multBase @ByteString sk1+ Just sk2 <- forAll $ toScalar <$> G.bytes scalarSize+ let pk2 = multBase @ByteString sk2++ let mshared1 = mult @ByteString pk2 sk1+ let mshared2 = mult @ByteString pk1 sk2+ case (mshared1, mshared2) of+ (Just shared1, Just shared2) -> shared1 === shared2+ _ -> discard
+ test/Test/NaCl/Secretbox.hs view
@@ -0,0 +1,113 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}++module Test.NaCl.Secretbox where++import Hedgehog (Property, forAll, property, tripping)+import Test.HUnit ((@?=), Assertion)++import Data.ByteString (ByteString)++import qualified Data.ByteString as BS+import qualified Libsodium as Na++import qualified Hedgehog.Gen as G+import qualified Hedgehog.Range as R++import qualified NaCl.Secretbox as Secretbox+++keySize :: R.Range Int+keySize = R.singleton $ fromIntegral Na.crypto_secretbox_keybytes++nonceSize :: R.Range Int+nonceSize = R.singleton $ fromIntegral Na.crypto_secretbox_noncebytes+++hprop_encode_decode :: Property+hprop_encode_decode = property $ do+ keyBytes <- forAll $ G.bytes keySize+ let Just key = Secretbox.toKey keyBytes+ nonceBytes <- forAll $ G.bytes nonceSize+ let Just nonce = Secretbox.toNonce nonceBytes+ 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 vector from+-- https://github.com/jedisct1/libsodium/blob/f911b56650b680ecfc5d32b11b090849fc2b5f92/test/default/secretbox.c++exampleKey :: ByteString+exampleKey = BS.pack $+ [ 0x1b, 0x27, 0x55, 0x64, 0x73, 0xe9, 0x85+ , 0xd4, 0x62, 0xcd, 0x51, 0x19, 0x7a, 0x9a+ , 0x46, 0xc7, 0x60, 0x09, 0x54, 0x9e, 0xac+ , 0x64, 0x74, 0xf2, 0x06, 0xc4, 0xee, 0x08+ , 0x44, 0xf6, 0x83, 0x89+ ]++exampleNonce :: ByteString+exampleNonce = BS.pack $+ [ 0x69, 0x69, 0x6e, 0xe9, 0x55, 0xb6+ , 0x2b, 0x73, 0xcd, 0x62, 0xbd, 0xa8+ , 0x75, 0xfc, 0x73, 0xd6, 0x82, 0x19+ , 0xe0, 0x03, 0x6b, 0x7a, 0x0b, 0x37+ ]++exampleMsg :: ByteString+exampleMsg = BS.pack $+ [ 0xbe, 0x07, 0x5f, 0xc5+ , 0x3c, 0x81, 0xf2, 0xd5, 0xcf, 0x14, 0x13, 0x16, 0xeb, 0xeb, 0x0c, 0x7b+ , 0x52, 0x28, 0xc5, 0x2a, 0x4c, 0x62, 0xcb, 0xd4, 0x4b, 0x66, 0x84, 0x9b+ , 0x64, 0x24, 0x4f, 0xfc, 0xe5, 0xec, 0xba, 0xaf, 0x33, 0xbd, 0x75, 0x1a+ , 0x1a, 0xc7, 0x28, 0xd4, 0x5e, 0x6c, 0x61, 0x29, 0x6c, 0xdc, 0x3c, 0x01+ , 0x23, 0x35, 0x61, 0xf4, 0x1d, 0xb6, 0x6c, 0xce, 0x31, 0x4a, 0xdb, 0x31+ , 0x0e, 0x3b, 0xe8, 0x25, 0x0c, 0x46, 0xf0, 0x6d, 0xce, 0xea, 0x3a, 0x7f+ , 0xa1, 0x34, 0x80, 0x57, 0xe2, 0xf6, 0x55, 0x6a, 0xd6, 0xb1, 0x31, 0x8a+ , 0x02, 0x4a, 0x83, 0x8f, 0x21, 0xaf, 0x1f, 0xde, 0x04, 0x89, 0x77, 0xeb+ , 0x48, 0xf5, 0x9f, 0xfd, 0x49, 0x24, 0xca, 0x1c, 0x60, 0x90, 0x2e, 0x52+ , 0xf0, 0xa0, 0x89, 0xbc, 0x76, 0x89, 0x70, 0x40, 0xe0, 0x82, 0xf9, 0x37+ , 0x76, 0x38, 0x48, 0x64, 0x5e, 0x07, 0x05+ ]++exampleCt :: ByteString+exampleCt = BS.pack $+ [ 0xf3, 0xff, 0xc7, 0x70, 0x3f, 0x94, 0x00, 0xe5+ , 0x2a, 0x7d, 0xfb, 0x4b, 0x3d, 0x33, 0x05, 0xd9+ , 0x8e, 0x99, 0x3b, 0x9f, 0x48, 0x68, 0x12, 0x73+ , 0xc2, 0x96, 0x50, 0xba, 0x32, 0xfc, 0x76, 0xce+ , 0x48, 0x33, 0x2e, 0xa7, 0x16, 0x4d, 0x96, 0xa4+ , 0x47, 0x6f, 0xb8, 0xc5, 0x31, 0xa1, 0x18, 0x6a+ , 0xc0, 0xdf, 0xc1, 0x7c, 0x98, 0xdc, 0xe8, 0x7b+ , 0x4d, 0xa7, 0xf0, 0x11, 0xec, 0x48, 0xc9, 0x72+ , 0x71, 0xd2, 0xc2, 0x0f, 0x9b, 0x92, 0x8f, 0xe2+ , 0x27, 0x0d, 0x6f, 0xb8, 0x63, 0xd5, 0x17, 0x38+ , 0xb4, 0x8e, 0xee, 0xe3, 0x14, 0xa7, 0xcc, 0x8a+ , 0xb9, 0x32, 0x16, 0x45, 0x48, 0xe5, 0x26, 0xae+ , 0x90, 0x22, 0x43, 0x68, 0x51, 0x7a, 0xcf, 0xea+ , 0xbd, 0x6b, 0xb3, 0x73, 0x2b, 0xc0, 0xe9, 0xda+ , 0x99, 0x83, 0x2b, 0x61, 0xca, 0x01, 0xb6, 0xde+ , 0x56, 0x24, 0x4a, 0x9e, 0x88, 0xd5, 0xf9, 0xb3+ , 0x79, 0x73, 0xf6, 0x22, 0xa4, 0x3d, 0x14, 0xa6+ , 0x59, 0x9b, 0x1f, 0x65, 0x4c, 0xb4, 0x5a, 0x74+ , 0xe3, 0x55, 0xa5+ ]++unit_example_create :: Assertion+unit_example_create = do+ let Just key = Secretbox.toKey exampleKey+ let Just nonce = Secretbox.toNonce exampleNonce+ Secretbox.create key nonce exampleMsg @?= exampleCt++unit_example_open :: Assertion+unit_example_open = do+ let Just key = Secretbox.toKey exampleKey+ let Just nonce = Secretbox.toNonce exampleNonce+ Secretbox.open key nonce exampleCt @?= Just exampleMsg
+ test/Test/NaCl/Sign.hs view
@@ -0,0 +1,199 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}++module Test.NaCl.Sign where++import Hedgehog (Property, forAll, property, tripping)+import Hedgehog.Internal.Property (forAllT)+import Test.HUnit ((@?=), Assertion)++import Data.ByteString (ByteString)+import Data.ByteString.Base16 (decodeBase16)+import Data.Either (fromRight)+import Control.Monad.IO.Class (liftIO)++import qualified Hedgehog.Gen as G+import qualified Hedgehog.Range as R++import qualified NaCl.Sign as Sign+++hprop_encode_decode :: Property+hprop_encode_decode = property $ do+ (pk, sk) <- forAllT $ liftIO $ Sign.keypair+ 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+++-- Test vectors from RFC8032.+--+-- Libsodium sk is 64 bytes and consists of 32 bytes of sk+-- followed by 32 bytes of pk.++unit_rfc8032_test1 :: Assertion+unit_rfc8032_test1 = do+ let+ Just sk = Sign.toSecretKey $ fromRight (error "impossible") . decodeBase16 $ mconcat+ [ "9d61b19deffd5a60ba844af492ec2cc4"+ , "4449c5697b326919703bac031cae7f60"+ , "d75a980182b10ab7d54bfed3c964073a"+ , "0ee172f3daa62325af021a68f707511a"+ ]+ msg = ""+ sig = fromRight (error "impossible") . decodeBase16 $ mconcat+ [ "e5564300c360ac729086e2cc806e828a"+ , "84877f1eb8e5d974d873e06522490155"+ , "5fb8821590a33bacc61e39701cf9b46b"+ , "d25bf5f0595bbe24655141438e7a100b"+ ]+ Sign.create sk msg @?= sig <> msg++unit_rfc8032_test2 :: Assertion+unit_rfc8032_test2 = do+ let+ Just sk = Sign.toSecretKey $ fromRight (error "impossible") . decodeBase16 $ mconcat+ [ "4ccd089b28ff96da9db6c346ec114e0f"+ , "5b8a319f35aba624da8cf6ed4fb8a6fb"+ , "3d4017c3e843895a92b70aa74d1b7ebc"+ , "9c982ccf2ec4968cc0cd55f12af4660c"+ ]+ msg = fromRight (error "impossible") . decodeBase16 $ "72"+ sig = fromRight (error "impossible") . decodeBase16 $ mconcat+ [ "92a009a9f0d4cab8720e820b5f642540"+ , "a2b27b5416503f8fb3762223ebdb69da"+ , "085ac1e43e15996e458f3613d0f11d8c"+ , "387b2eaeb4302aeeb00d291612bb0c00"+ ]+ Sign.create sk msg @?= sig <> msg++unit_rfc8032_test3 :: Assertion+unit_rfc8032_test3 = do+ let+ Just sk = Sign.toSecretKey $ fromRight (error "impossible") . decodeBase16 $ mconcat+ [ "c5aa8df43f9f837bedb7442f31dcb7b1"+ , "66d38535076f094b85ce3a2e0b4458f7"+ , "fc51cd8e6218a1a38da47ed00230f058"+ , "0816ed13ba3303ac5deb911548908025"+ ]+ msg = fromRight (error "impossible") . decodeBase16 $ "af82"+ sig = fromRight (error "impossible") . decodeBase16 $ mconcat+ [ "6291d657deec24024827e69c3abe01a3"+ , "0ce548a284743a445e3680d7db5ac3ac"+ , "18ff9b538d16f290ae67f760984dc659"+ , "4a7c15e9716ed28dc027beceea1ec40a"+ ]+ Sign.create sk msg @?= sig <> msg++unit_rfc8032_test1024 :: Assertion+unit_rfc8032_test1024 = do+ let+ Just sk = Sign.toSecretKey $ fromRight (error "impossible") . decodeBase16 $ mconcat+ [ "f5e5767cf153319517630f226876b86c"+ , "8160cc583bc013744c6bf255f5cc0ee5"+ , "278117fc144c72340f67d0f2316e8386"+ , "ceffbf2b2428c9c51fef7c597f1d426e"+ ]+ msg = fromRight (error "impossible") . decodeBase16 $ mconcat+ [ "08b8b2b733424243760fe426a4b54908"+ , "632110a66c2f6591eabd3345e3e4eb98"+ , "fa6e264bf09efe12ee50f8f54e9f77b1"+ , "e355f6c50544e23fb1433ddf73be84d8"+ , "79de7c0046dc4996d9e773f4bc9efe57"+ , "38829adb26c81b37c93a1b270b20329d"+ , "658675fc6ea534e0810a4432826bf58c"+ , "941efb65d57a338bbd2e26640f89ffbc"+ , "1a858efcb8550ee3a5e1998bd177e93a"+ , "7363c344fe6b199ee5d02e82d522c4fe"+ , "ba15452f80288a821a579116ec6dad2b"+ , "3b310da903401aa62100ab5d1a36553e"+ , "06203b33890cc9b832f79ef80560ccb9"+ , "a39ce767967ed628c6ad573cb116dbef"+ , "efd75499da96bd68a8a97b928a8bbc10"+ , "3b6621fcde2beca1231d206be6cd9ec7"+ , "aff6f6c94fcd7204ed3455c68c83f4a4"+ , "1da4af2b74ef5c53f1d8ac70bdcb7ed1"+ , "85ce81bd84359d44254d95629e9855a9"+ , "4a7c1958d1f8ada5d0532ed8a5aa3fb2"+ , "d17ba70eb6248e594e1a2297acbbb39d"+ , "502f1a8c6eb6f1ce22b3de1a1f40cc24"+ , "554119a831a9aad6079cad88425de6bd"+ , "e1a9187ebb6092cf67bf2b13fd65f270"+ , "88d78b7e883c8759d2c4f5c65adb7553"+ , "878ad575f9fad878e80a0c9ba63bcbcc"+ , "2732e69485bbc9c90bfbd62481d9089b"+ , "eccf80cfe2df16a2cf65bd92dd597b07"+ , "07e0917af48bbb75fed413d238f5555a"+ , "7a569d80c3414a8d0859dc65a46128ba"+ , "b27af87a71314f318c782b23ebfe808b"+ , "82b0ce26401d2e22f04d83d1255dc51a"+ , "ddd3b75a2b1ae0784504df543af8969b"+ , "e3ea7082ff7fc9888c144da2af58429e"+ , "c96031dbcad3dad9af0dcbaaaf268cb8"+ , "fcffead94f3c7ca495e056a9b47acdb7"+ , "51fb73e666c6c655ade8297297d07ad1"+ , "ba5e43f1bca32301651339e22904cc8c"+ , "42f58c30c04aafdb038dda0847dd988d"+ , "cda6f3bfd15c4b4c4525004aa06eeff8"+ , "ca61783aacec57fb3d1f92b0fe2fd1a8"+ , "5f6724517b65e614ad6808d6f6ee34df"+ , "f7310fdc82aebfd904b01e1dc54b2927"+ , "094b2db68d6f903b68401adebf5a7e08"+ , "d78ff4ef5d63653a65040cf9bfd4aca7"+ , "984a74d37145986780fc0b16ac451649"+ , "de6188a7dbdf191f64b5fc5e2ab47b57"+ , "f7f7276cd419c17a3ca8e1b939ae49e4"+ , "88acba6b965610b5480109c8b17b80e1"+ , "b7b750dfc7598d5d5011fd2dcc5600a3"+ , "2ef5b52a1ecc820e308aa342721aac09"+ , "43bf6686b64b2579376504ccc493d97e"+ , "6aed3fb0f9cd71a43dd497f01f17c0e2"+ , "cb3797aa2a2f256656168e6c496afc5f"+ , "b93246f6b1116398a346f1a641f3b041"+ , "e989f7914f90cc2c7fff357876e506b5"+ , "0d334ba77c225bc307ba537152f3f161"+ , "0e4eafe595f6d9d90d11faa933a15ef1"+ , "369546868a7f3a45a96768d40fd9d034"+ , "12c091c6315cf4fde7cb68606937380d"+ , "b2eaaa707b4c4185c32eddcdd306705e"+ , "4dc1ffc872eeee475a64dfac86aba41c"+ , "0618983f8741c5ef68d3a101e8a3b8ca"+ , "c60c905c15fc910840b94c00a0b9d0"+ ]+ sig = fromRight (error "impossible") . decodeBase16 $ mconcat+ [ "0aab4c900501b3e24d7cdf4663326a3a"+ , "87df5e4843b2cbdb67cbf6e460fec350"+ , "aa5371b1508f9f4528ecea23c436d94b"+ , "5e8fcd4f681e30a6ac00a9704a188a03"+ ]+ Sign.create sk msg @?= sig <> msg+++unit_rfc8032_test_sha_abc :: Assertion+unit_rfc8032_test_sha_abc = do+ let+ Just sk = Sign.toSecretKey $ fromRight (error "impossible") . decodeBase16 $ mconcat+ [ "833fe62409237b9d62ec77587520911e"+ , "9a759cec1d19755b7da901b96dca3d42"+ , "ec172b93ad5e563bf4932c70e1245034"+ , "c35467ef2efd4d64ebf819683467e2bf"+ ]+ msg = fromRight (error "impossible") . decodeBase16 $ mconcat+ [ "ddaf35a193617abacc417349ae204131"+ , "12e6fa4e89a97ea20a9eeee64b55d39a"+ , "2192992a274fc1a836ba3c23a3feebbd"+ , "454d4423643ce80e2a9ac94fa54ca49f"+ ]+ sig = fromRight (error "impossible") . decodeBase16 $ mconcat+ [ "dc2a4459e7369633a52b1bf277839a00"+ , "201009a3efbf3ecb69bea2186c26b589"+ , "09351fc9ac90b3ecfdfbc7c66431e030"+ , "3dca179c138ac17ad9bef1177331a704"+ ]+ Sign.create sk msg @?= sig <> msg
+ test/Test/NaCl/Stream.hs view
@@ -0,0 +1,78 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}++module Test.NaCl.Stream where++import Hedgehog (Property, forAll, property, tripping)+import Test.HUnit ((@?=), Assertion)++import Data.ByteArray.Sized (unSizedByteArray)+import Data.ByteString (ByteString)+import Data.ByteString.Base16 (decodeBase16)+import Data.Either (fromRight)++import qualified Data.ByteString as BS+import qualified Libsodium as Na++import qualified Hedgehog.Gen as G+import qualified Hedgehog.Range as R++import NaCl.Hash (sha256)++import qualified NaCl.Stream as Stream+++keySize :: R.Range Int+keySize = R.singleton $ fromIntegral Na.crypto_stream_keybytes++nonceSize :: R.Range Int+nonceSize = R.singleton $ fromIntegral Na.crypto_stream_noncebytes+++hprop_xor_twice :: Property+hprop_xor_twice = property $ do+ keyBytes <- forAll $ G.bytes keySize+ let Just key = Stream.toKey keyBytes+ nonceBytes <- forAll $ G.bytes nonceSize+ let Just nonce = Stream.toNonce nonceBytes+ 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 = Stream.xor key nonce msg :: ByteString+ decodeBs key nonce ct = Just $ Stream.xor key nonce ct :: Maybe ByteString+++-- Test vector from+-- https://github.com/jedisct1/libsodium/blob/f911b56650b680ecfc5d32b11b090849fc2b5f92/test/default/stream.c++exampleKey :: ByteString+exampleKey = BS.pack $+ [ 0x1b, 0x27, 0x55, 0x64, 0x73, 0xe9, 0x85+ , 0xd4, 0x62, 0xcd, 0x51, 0x19, 0x7a, 0x9a+ , 0x46, 0xc7, 0x60, 0x09, 0x54, 0x9e, 0xac+ , 0x64, 0x74, 0xf2, 0x06, 0xc4, 0xee, 0x08+ , 0x44, 0xf6, 0x83, 0x89+ ]++exampleNonce :: ByteString+exampleNonce = BS.pack $+ [ 0x69, 0x69, 0x6e, 0xe9, 0x55, 0xb6+ , 0x2b, 0x73, 0xcd, 0x62, 0xbd, 0xa8+ , 0x75, 0xfc, 0x73, 0xd6, 0x82, 0x19+ , 0xe0, 0x03, 0x6b, 0x7a, 0x0b, 0x37+ ]++exampleStreamSha256 :: ByteString+exampleStreamSha256 = fromRight (error "impossible") . decodeBase16 $+ "662b9d0e3463029156069b12f918691a98f7dfb2ca0393c96bbfc6b1fbd630a2"++unit_example_generate :: Assertion+unit_example_generate = do+ let Just key = Stream.toKey exampleKey+ let Just nonce = Stream.toNonce exampleNonce+ let stream = unSizedByteArray $ Stream.generate @4194304 key nonce :: ByteString+ unSizedByteArray (sha256 stream) @?= exampleStreamSha256
+ test/Test/NaCl/Verify.hs view
@@ -0,0 +1,53 @@+-- SPDX-FileCopyrightText: 2020 Serokell+--+-- SPDX-License-Identifier: MPL-2.0++{-# LANGUAGE AllowAmbiguousTypes #-}+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}++module Test.NaCl.Verify where++import Hedgehog (assert, forAll, property)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.Hedgehog (testProperty)++import Data.ByteArray.Sized (sizedByteArray)+import Data.Proxy (Proxy (Proxy))+import GHC.TypeLits (natVal)++import qualified Hedgehog.Gen as G+import qualified Hedgehog.Range as R++import NaCl.Verify (eq, NaClComparable)+++crypto_verify_test :: forall n. NaClComparable n => TestTree+crypto_verify_test =+ testGroup ("crypto_verify_" <> show n) $+ [ testProperty "self random" $ property $ do+ xBytes <- forAll $ G.bytes (R.singleton n)+ let Just x = sizedByteArray @n xBytes+ assert $ x `eq` x+ , testProperty "two random" $ property $ do+ xBytes <- forAll $ G.bytes (R.singleton n)+ let Just x = sizedByteArray @n xBytes+ yBytes <- forAll $ G.bytes (R.singleton n)+ let Just y = sizedByteArray @n yBytes+ assert $ (x `eq` y) == (x == y)+ , testProperty "checks last" $ property $ do+ prefix <- forAll $ G.bytes (R.singleton $ n - 1)+ xLast <- forAll $ G.bytes (R.singleton 1)+ let Just x = sizedByteArray @n (prefix <> xLast)+ yLast <- forAll $ G.bytes (R.singleton 1)+ let Just y = sizedByteArray @n (prefix <> yLast)+ assert $ (x `eq` y) == (xLast == yLast)+ ]+ where+ n = fromIntegral $ natVal (Proxy @n)+++test_16 :: TestTree+test_16 = crypto_verify_test @16++test_32 :: TestTree+test_32 = crypto_verify_test @32