diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,9 +5,10 @@
 ### Added
 
 * `sodiumInit`
-* Random bytes generation: `Crypto.Random`
-* Key derivation: `Crypto.Key.derive` and `Crypto.Key.rederive`
-* MAC: `Crypto.Mac`, `Crypto.Mac.Lazy`
-* Random nonce generation: `Crypto.Nonce`
-* Public-key signatures: `Crypto.Sign`
-* Keypair generation from seed: `Crypto.Sign` and `Crypto.Encrypt.Public`
+* Random bytes generation: `Crypto.Sodium.Random`
+* Key derivation: `Crypto.Sodium.Key.derive` and `Crypto.Sodium.Key.rederive`
+* MAC: `Crypto.Sodium.Mac`, `Crypto.Sodium.Mac.Lazy`
+* Random nonce generation: `Crypto.Sodium.Nonce`
+* Public-key signatures: `Crypto.Sodium.Sign`
+* Keypair generation from seed: `Crypto.Sodium.Sign` and `Crypto.Sodium.Encrypt.Public`
+* Hash: `Crypto.Sodium.Hash.blake2b`, `Crypto.Sodium.Hash.blake2bWithKey`, `Crypto.Sodium.Hash.sha256`, `Crypto.Sodium.Hash.sha512`
diff --git a/crypto-sodium.cabal b/crypto-sodium.cabal
--- a/crypto-sodium.cabal
+++ b/crypto-sodium.cabal
@@ -3,11 +3,9 @@
 -- This file has been generated from package.yaml by hpack version 0.34.4.
 --
 -- see: https://github.com/sol/hpack
---
--- hash: 25a103374b2b14f150bf4f1ccb243ed60f601e794e2345bee0adec07b08f3a47
 
 name:           crypto-sodium
-version:        0.0.4.0
+version:        0.0.5.0
 synopsis:       Easy-and-safe-to-use high-level cryptography based on Sodium
 description:    This is a collection of high-level cryptographic primitives based on
                 <https://libsodium.io/ Sodium>, spiced up with extra type-safety
@@ -37,22 +35,23 @@
                 .
                 == Library initialisation
                 .
-                * "Crypto.Init"
+                * "Crypto.Sodium.Init"
                 .
                 == Secret-key cryptography
                 .
-                * Authenticated symmetric-key encryption: "Crypto.Encrypt.Symmetric"
-                * Message authentication codes: "Crypto.Mac"
+                * Authenticated symmetric-key encryption: "Crypto.Sodium.Encrypt.Symmetric"
+                * Message authentication codes: "Crypto.Sodium.Mac"
                 .
                 == Public-key cryptography
                 .
-                * Authenticated public-key encryption: "Crypto.Encrypt.Public"
-                * Public-key signatures: "Crypto.Sign"
+                * Authenticated public-key encryption: "Crypto.Sodium.Encrypt.Public"
+                * Public-key signatures: "Crypto.Sodium.Sign"
                 .
                 == Additional primitives
                 .
-                * Key derivation and generation: "Crypto.Key"
-                * Cryptographically-secure random: "Crypto.Random"
+                * Key derivation and generation: "Crypto.Sodium.Key"
+                * Cryptographically-secure random: "Crypto.Sodium.Random"
+                * Hashing: "Crypto.Sodium.Hash"
 category:       Cryptography
 homepage:       https://github.com/serokell/haskell-crypto#readme
 bug-reports:    https://github.com/serokell/haskell-crypto/issues
@@ -72,19 +71,21 @@
 
 library
   exposed-modules:
-      Crypto.Encrypt.Public
-      Crypto.Encrypt.Symmetric
-      Crypto.Encrypt.Symmetric.Stream
-      Crypto.Init
-      Crypto.Internal.Random
-      Crypto.Internal.Verify
-      Crypto.Key
-      Crypto.Key.Internal
-      Crypto.Mac
-      Crypto.Nonce
-      Crypto.Pwhash.Internal
-      Crypto.Random
-      Crypto.Sign
+      Crypto.Sodium.Encrypt.Public
+      Crypto.Sodium.Encrypt.Symmetric
+      Crypto.Sodium.Encrypt.Symmetric.Stream
+      Crypto.Sodium.Hash
+      Crypto.Sodium.Hash.Internal
+      Crypto.Sodium.Init
+      Crypto.Sodium.Internal.Random
+      Crypto.Sodium.Internal.Verify
+      Crypto.Sodium.Key
+      Crypto.Sodium.Key.Internal
+      Crypto.Sodium.Mac
+      Crypto.Sodium.Nonce
+      Crypto.Sodium.Pwhash.Internal
+      Crypto.Sodium.Random
+      Crypto.Sodium.Sign
   other-modules:
       Paths_crypto_sodium
   hs-source-dirs:
@@ -106,11 +107,11 @@
   ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints
   build-depends:
       NaCl >=0.0.4.0 && <0.1
-    , base >=4.10 && <4.15
+    , base >=4.10 && <4.16
     , bytestring >=0.9 && <0.11
     , cereal >=0.1 && <0.6
     , libsodium >=1.0.11 && <2
-    , memory >=0.14.15 && <0.16
+    , memory >=0.14.15 && <0.17
     , random >=1.0 && <1.3
     , safe-exceptions ==0.1.*
   default-language: Haskell2010
@@ -119,14 +120,15 @@
   type: exitcode-stdio-1.0
   main-is: Test.hs
   other-modules:
-      Test.Crypto.Encrypt.Public
-      Test.Crypto.Encrypt.Symmetric
-      Test.Crypto.Gen
-      Test.Crypto.Key.Derivation
-      Test.Crypto.Nonce
-      Test.Crypto.Pwhash
-      Test.Crypto.Random
-      Test.Crypto.Sign
+      Test.Crypto.Sodium.Encrypt.Public
+      Test.Crypto.Sodium.Encrypt.Symmetric
+      Test.Crypto.Sodium.Gen
+      Test.Crypto.Sodium.Hash
+      Test.Crypto.Sodium.Key.Derivation
+      Test.Crypto.Sodium.Nonce
+      Test.Crypto.Sodium.Pwhash
+      Test.Crypto.Sodium.Random
+      Test.Crypto.Sodium.Sign
       Paths_crypto_sodium
   hs-source-dirs:
       test
@@ -149,14 +151,14 @@
       tasty-discover:tasty-discover
   build-depends:
       HUnit
-    , base >=4.10 && <4.15
+    , base >=4.10 && <4.16
     , base16 >=0.1.1 && <0.4
     , bytestring >=0.9 && <0.11
     , crypto-sodium
     , deepseq
     , hedgehog
     , libsodium >=1.0.11 && <2
-    , memory >=0.14.15 && <0.16
+    , memory >=0.14.15 && <0.17
     , safe-exceptions ==0.1.*
     , tasty
     , tasty-hedgehog
diff --git a/lib/Crypto/Encrypt/Public.hs b/lib/Crypto/Encrypt/Public.hs
deleted file mode 100644
--- a/lib/Crypto/Encrypt/Public.hs
+++ /dev/null
@@ -1,154 +0,0 @@
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
-{-# OPTIONS_HADDOCK not-home #-}
-
--- ! This module merely re-exports definitions from the corresponding
--- ! module in NaCl and alters the Haddock to make it more specific
--- ! to crypto-sodium. So, the docs should be kept more-or-less in sync.
-
--- | Public-key authenticated encryption.
---
--- It is best to import this module qualified:
---
--- @
--- import qualified Crypto.Encrypt.Public as Public
---
--- encrypted = Public.'encrypt' pk sk nonce message
--- decrypted = Public.'decrypt' pk sk nonce encrypted
--- @
---
--- A box is an abstraction from NaCl. One way to think about it
--- is to imagine that you are putting data into a box protected by
--- the receiver’s public key and signed by your private key. The
--- receive will then be able to 'open' it using their private key
--- and your public key.
---
--- Note that this means that you need to exchange your public keys
--- in advance. It might seem strange at first that the receiver
--- needs to know your public key too, but this is actually very important
--- as otherwise the receiver would not be able to have any guarantees
--- regarding the source or the integrity of the data.
-module Crypto.Encrypt.Public
-  (
-  -- * Keys
-    PublicKey
-  , toPublicKey
-  , SecretKey
-  , toSecretKey
-  , keypair
-  , keypairFromSeed
-  , unsafeKeypairFromSeed
-
-  -- * Nonce
-  , Nonce
-  , toNonce
-
-  -- * Encryption/decryption
-  , encrypt
-  , decrypt
-  ) where
-
-import Data.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes, withByteArray)
-import Data.ByteArray.Sized as Sized (SizedByteArray, alloc, allocRet)
-import Data.ByteString (ByteString)
-import Data.Functor (void)
-import Data.Proxy (Proxy(..))
-import System.IO.Unsafe (unsafePerformIO)
-
-import qualified Libsodium as Na
-
-import NaCl.Box
-  (Nonce, PublicKey, SecretKey, keypair, toNonce, toPublicKey, toSecretKey)
-import qualified NaCl.Box as NaCl.Box
-
-
--- | Encrypt a message.
---
--- @
--- encrypted = Public.encrypt pk sk nonce message
--- @
---
--- *   @pk@ is the receiver’s public key, used for encryption.
---     @sk@ is the sender’s secret key, used for authentication.
---
---     These are generated using 'keypair' and are supposed to be exchanged
---     in advance. Both parties need to know their own secret key and the other’s
---     public key.
---
--- *   @nonce@ is an extra noise that ensures that is required for security.
---     See "Crypto.Nonce" for how to work with it.
---
--- *   @message@ is the data you are encrypting.
---
--- This function adds authentication data, so if anyone modifies the cyphertext,
--- 'decrypt' will refuse to decrypt it.
-encrypt
-  ::  ( ByteArrayAccess pkBytes, ByteArrayAccess skBytes
-      , ByteArrayAccess nonceBytes
-      , ByteArrayAccess ptBytes, ByteArray ctBytes
-      )
-  => PublicKey pkBytes  -- ^ Receiver’s public key
-  -> SecretKey skBytes  -- ^ Sender’s secret key
-  -> Nonce nonceBytes  -- ^ Nonce
-  -> ptBytes -- ^ Plaintext message
-  -> ctBytes
-encrypt = NaCl.Box.create
-
-
--- | Decrypt a message.
---
--- @
--- decrypted = Public.decrypt sk pk nonce encrypted
--- @
---
--- * @sk@ is the receiver’s secret key, used for decription.
--- * @pk@ is the sender’s public key, used for authentication.
--- * @nonce@ is the same that was used for encryption.
--- * @encrypted@ is the output of 'encrypt'.
---
--- This function will return @Nothing@ if the encrypted message was tampered
--- with after it was encrypted.
-decrypt
-  ::  ( ByteArrayAccess skBytes, ByteArrayAccess pkBytes
-      , ByteArrayAccess nonceBytes
-      , ByteArray ptBytes, ByteArrayAccess ctBytes
-      )
-  => SecretKey skBytes  -- ^ Receiver’s secret key
-  -> PublicKey pkBytes  -- ^ Sender’s public key
-  -> Nonce nonceBytes  -- ^ Nonce
-  -> ctBytes -- ^ Encrypted message (cyphertext)
-  -> Maybe ptBytes
-decrypt = NaCl.Box.open
-
-
--- | Seed for deterministically generating a keypair.
---
--- In accordance with Libsodium's documentation, the seed must be of size
--- @Na.CRYPTO_BOX_SEEDBYTES@.
---
--- This type is parametrised by the actual data type that contains
--- bytes. This can be, for example, a @ByteString@.
-type Seed a = SizedByteArray Na.CRYPTO_BOX_SEEDBYTES a
-
-
--- | Generate a new 'SecretKey' together with its 'PublicKey' from a given seed.
-keypairFromSeed
-  :: ByteArrayAccess seed
-  => Seed seed
-  -> IO (PublicKey ByteString, SecretKey ScrubbedBytes)
-keypairFromSeed seed = do
-  allocRet Proxy $ \skPtr ->
-    alloc $ \pkPtr ->
-    withByteArray seed $ \sdPtr ->
-    -- always returns 0, so we don’t check it
-    void $ Na.crypto_box_seed_keypair pkPtr skPtr sdPtr
-
--- | Generate a new 'SecretKey' together with its 'PublicKey' from a given seed,
--- in a pure context.
-unsafeKeypairFromSeed
-  :: ByteArrayAccess seed
-  => Seed seed
-  -> (PublicKey ByteString, SecretKey ScrubbedBytes)
-unsafeKeypairFromSeed = unsafePerformIO . keypairFromSeed
diff --git a/lib/Crypto/Encrypt/Symmetric.hs b/lib/Crypto/Encrypt/Symmetric.hs
deleted file mode 100644
--- a/lib/Crypto/Encrypt/Symmetric.hs
+++ /dev/null
@@ -1,94 +0,0 @@
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
-{-# OPTIONS_HADDOCK not-home #-}
-
--- ! This module merely re-exports definitions from the corresponding
--- ! module in NaCl and alters the Haddock to make it more specific
--- ! to crypto-sodium. So, the docs should be kept more-or-less in sync.
-
--- | Symmetric authenticated encryption.
---
--- It is best to import this module qualified:
---
--- @
--- import qualified Crypto.Encrypt.Symmetric as Symmetric
---
--- encrypted = Symmetric.'encrypt' key nonce message
--- decrypted = Symmetric.'decrypt' key nonce encrypted
--- @
---
--- In NaCl this is know as a “Secretbox”. One way to think about it
--- is to imagine that you are putting data into a box protected by a
--- secret key. You “create” such a box using 'encrypt', store it somewhere
--- (it is just a sequence of bytes), and when you need it in the
--- future, you “open” it with 'decrypt' using the same secret key.
-module Crypto.Encrypt.Symmetric
-  (
-  -- * Keys
-    Key
-  , toKey
-
-  -- * Nonce
-  , Nonce
-  , toNonce
-
-  -- * Encryption/decryption
-  , encrypt
-  , decrypt
-  ) where
-
-import NaCl.Secretbox (Key, Nonce, toKey, toNonce)
-import Data.ByteArray (ByteArray, ByteArrayAccess)
-
-import qualified NaCl.Secretbox as NaCl.Secretbox
-
-
--- | Encrypt a message.
---
--- @
--- encrypted = Symmetric.encrypt key nonce message
--- @
---
--- *   @key@ is the secret key used for encryption. See "Crypto.Key" for how
---     to get one.
---
--- *   @nonce@ is an extra noise that is required for security.
---     See "Crypto.Nonce" for how to work with it.
---
--- *   @message@ is the data you are encrypting.
---
--- This function adds authentication data, so if anyone modifies the cyphertext,
--- 'open' will refuse to decrypt it.
-encrypt
-  ::  ( ByteArrayAccess keyBytes, ByteArrayAccess nonceBytes
-      , ByteArrayAccess ptBytes, ByteArray ctBytes
-      )
-  => Key keyBytes  -- ^ Secret key
-  -> Nonce nonceBytes  -- ^ Nonce
-  -> ptBytes -- ^ Plaintext message
-  -> ctBytes
-encrypt = NaCl.Secretbox.create
-
-
--- | Decrypt a message.
---
--- @
--- decrypted = Symmetric.decrypt key nonce encrypted
--- @
---
--- * @key@ and @nonce@ are the same that were used for encryption.
--- * @encrypted@ is the output of 'create'.
---
--- This function will return @Nothing@ if the encrypted message was tampered
--- with after it was encrypted.
-decrypt
-  ::  ( ByteArrayAccess keyBytes, ByteArrayAccess nonceBytes
-      , ByteArray ptBytes, ByteArrayAccess ctBytes
-      )
-  => Key keyBytes  -- ^ Secret key
-  -> Nonce nonceBytes  -- ^ Nonce
-  -> ctBytes -- ^ Encrypted message (cyphertext)
-  -> Maybe ptBytes
-decrypt = NaCl.Secretbox.open
diff --git a/lib/Crypto/Encrypt/Symmetric/Stream.hs b/lib/Crypto/Encrypt/Symmetric/Stream.hs
deleted file mode 100644
--- a/lib/Crypto/Encrypt/Symmetric/Stream.hs
+++ /dev/null
@@ -1,41 +0,0 @@
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
-{-# OPTIONS_HADDOCK not-home #-}
-
--- | Symmetric authenticated encryption for streams.
---
--- This module provides generic types for Sodium-based streaming
--- encryption. It does not provide any functionality itself.
---
--- There are separate packages that actually implement this functionality
--- for specific streaming libraries:
---
--- * @crypto-sodium-streamly@ for @streamly@ streams.
-module Crypto.Encrypt.Symmetric.Stream
-  (
-  -- * Keys
-    Key
-  , toKey
-  ) where
-
-import Data.ByteArray (ByteArrayAccess)
-import Data.ByteArray.Sized (SizedByteArray, sizedByteArray)
-
-import qualified Libsodium as Na
-
-
--- | Encryption key that can be used for streaming symmetric encryption.
---
--- This type is parametrised by the actual data type that contains
--- bytes. This can be, for example, a @ByteString@, but, since this
--- is a secret key, it is better to use @ScrubbedBytes@.
-type Key a = SizedByteArray Na.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES a
-
--- | Make a 'Key' from an arbitrary byte array.
---
--- This function returns @Just@ if and only if the byte array has
--- the right length to be used as a key with a streaming symmetric encryption.
-toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)
-toKey = sizedByteArray
diff --git a/lib/Crypto/Init.hs b/lib/Crypto/Init.hs
deleted file mode 100644
--- a/lib/Crypto/Init.hs
+++ /dev/null
@@ -1,72 +0,0 @@
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
--- | Libsodium initialisation.
-
--- = Thread-safety #threadSafety#
---
--- Some of the Sodium (and NaCl) functions (those that generate random data)
--- are not thread-safe. All these functions are explicitly marked as such
--- in their Haddock documentation.
---
--- Calling 'sodiumInit' before they are used makes them thread-safe.
---
--- = Performance
---
--- Sodium contains multiple implementations of the primitives it provides.
--- There are generic implementations, that are used by default, and
--- multiple alternatives optimised for various platforms.
---
--- 'sodiumInit' will quickly benchmark all available implementations and choose
--- the best ones for each primitive.
-module Crypto.Init
-  ( sodiumInit
-  , SodiumInitException (..)
-  ) where
-
-import Control.Exception (Exception, throwIO)
-import Libsodium (sodium_init)
-
-
--- | Initialise libsodium.
---
--- This is just @sodium_init()@ from libsodium. Calling it before using
--- any Sodium functions is optional, but strongly recommended.
---
--- This function does the following:
---
--- 1. Open @\/dev\/urandom@ (on Unix) to make it accessible even after @chroot()@.
---
--- 2. Make all libsodium functions thread-safe.
---
--- 3. Benchmark different implementations of cryptographic primitives provided
--- and choose the best ones.
---
--- This function itself is thread-safe (since libsodium-1.0.11).
-sodiumInit :: IO ()
-sodiumInit = sodium_init >>= \case
-  0 ->
-    -- Success!
-    pure ()
-  1 ->
-    -- Already initialised, that’s ok.
-    pure ()
-  _ ->
-    -- If initialisation fails, using libsodium is unsafe, and there is
-    -- really nothing that can be done at this point and there is no way
-    -- to recover.
-    -- It would be nice to provide some helpful diagnostic here, but,
-    -- unfortunately, libsodium gives no information on the failure reason.
-    throwIO SodiumInitFailed
-
-
--- | Exception thrown by 'sodiumInit'.
-data SodiumInitException
-  = SodiumInitFailed  -- ^ libsodium failed to initialise.
-
-instance Show SodiumInitException where
-  show SodiumInitFailed =
-    "libsodium failed to initialise and is not safe to use"
-
-instance Exception SodiumInitException
diff --git a/lib/Crypto/Internal/Random.hs b/lib/Crypto/Internal/Random.hs
deleted file mode 100644
--- a/lib/Crypto/Internal/Random.hs
+++ /dev/null
@@ -1,31 +0,0 @@
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
--- | Generate simple insecure random data.
-module Crypto.Internal.Random
-  ( generateInsecure
-  ) where
-
-import Data.ByteArray.Sized (SizedByteArray)
-import Data.ByteString (ByteString)
-import GHC.TypeLits (KnownNat)
-
-import Crypto.Random (generate)
-
-
--- | Generate a sequence of random bytes.
---
--- The output of this function is NOT suitable for secret keys.
-generateInsecure
-  :: forall n. (KnownNat n)
-  => IO (SizedByteArray n ByteString)
-generateInsecure
-{-
-  = unsafeSizedByteArray . BS.pack . take len . randoms <$> newStdGen
-  where
-    len = fromIntegral $ natVal (Proxy :: Proxy n)
--}
-  -- Haddock above is actually a lie. We use the same random generator
-  -- as for keys, because, after benchmarking, it happens to be faster :/.
-  = generate
diff --git a/lib/Crypto/Internal/Verify.hs b/lib/Crypto/Internal/Verify.hs
deleted file mode 100644
--- a/lib/Crypto/Internal/Verify.hs
+++ /dev/null
@@ -1,35 +0,0 @@
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
--- | @crypto_verify_*@
-module Crypto.Internal.Verify
-  ( verifyBytes32
-  ) where
-
-import Data.ByteArray (ByteArrayAccess, withByteArray)
-import Data.ByteArray.Sized (SizedByteArray)
-
-import qualified Libsodium as Na
-
-
--- | Compare two byte arrays of length 32.
-verifyBytes32
-  :: ( ByteArrayAccess ba1
-     , ByteArrayAccess ba2
-     )
-  => SizedByteArray 32 ba1  -- ^ First byte array
-  -> SizedByteArray 32 ba2  -- ^ Second byte array
-  -> IO Bool
-verifyBytes32 bytes1 bytes2 =
-  withByteArray bytes1 $ \ptr1 ->
-  withByteArray bytes2 $ \ptr2 -> do
-    -- TODO: I have no idea what I am doing
-    --
-    -- - Sodium also checks that pointers are different?
-    -- - This kind of double comparison was added in
-    --   c5a9d46386f917aa0ff1bfb711450f9af1d79a17
-    --   (why?)
-    res1 <- Na.crypto_verify_32 ptr1 ptr2
-    res2 <- Na.sodium_memcmp ptr2 ptr1 32
-    pure $ res1 == 0 && res2 == 0
diff --git a/lib/Crypto/Key.hs b/lib/Crypto/Key.hs
deleted file mode 100644
--- a/lib/Crypto/Key.hs
+++ /dev/null
@@ -1,198 +0,0 @@
-{-# OPTIONS_GHC -Wno-redundant-constraints #-}
-{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE TypeOperators #-}
-
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
--- | This module gives different ways of obtaining secret keys.
---
--- = Key derivation (from a password)
---
--- Sometimes, instead of generating fresh random transient encryption keys,
--- you want an encryption key to be persistent and you don’t want to
--- store it anywhere – instead you ask the user to provide it.
--- Such a secret value known and entered by the user is usually called
--- a “password”.
---
--- However, passwords make terrible encryption keys because encryption keys:
---
--- 1. often need to have a specific exact length and
--- 2. need to be hard to guess (or brute-force).
---
--- Item 1 above can be easily ticked off by deriving the encryption key from
--- the password by applying a hash-function to it, however in order to
--- achieve 2 we need our “hash-function” to:
---
--- * be slow to compute (to make brute-forcing less feasible) and
--- * mix extra “noise” into the derivation process to make it harder
---   to pre-compute derived values in advance.
---
--- A construction that satisfies both requirements is called a
--- /key derivation function (KDF)/.
--- This module provides a convenient interface for deriving
--- secure keys from passwords by the way of one such KDF.
---
--- == Use
---
--- This module provides two functions: 'derive' and 'rederive'.
--- You can think of the entire process similar to how you set the password
--- on your account at some website once, and then use this password to log in.
---
--- When you derive a key for the first time (e.g. you ask the user to enter their
--- password twice, and then encrypt something), you use the 'derive' function,
--- which gives you the derived key and a /derivation slip/. The slip is not
--- secret, you can store it in plain text and, in fact, you /have to/ store it
--- in plaintext somewhere next to the encrypted data.
---
--- When you need to derive the key in the future (e.g. to decrypt some previously
--- encrypted data), you will need the user’s password (ask them) /and/ you
--- will need the original derivation slip, which you should have stored.
--- You pass these to 'rederive' and it will give you the same key.
---
--- @
--- import qualified Crypto.Key as Key
---
--- encrypt = do
---   password <- {- ask the user to enter their password -}
---   password2 <- {- ask the user to confirm their password -}
---   when (password /= password2 then) $ throwIO {- passwords do not match -}
---
---   let params = {- choose key derivation parameters -}
---   (key, slip) <- Key.derive params password
---
---   {- store slip (it is not secret) -}
---   {- encrypt data with key -}
---
--- decrypt = do
---   password <- {- ask the user to enter their password -}
---   slip <- {- get the stored slip -}
---
---   key <- Key.rederive slip password
---
---   {- decrypt data with key -}
--- @
---
--- = Random key generation
---
--- The 'generate' function is great at generating new secure secret keys.
-module Crypto.Key
-  ( type (!>=!)
-
-  -- * Key derivation
-  , Params (..)
-  , DerivationSlip
-  , derive
-  , rederive
-
-  -- * Random key generation
-  , generate
-  ) where
-
-import Data.ByteArray (ByteArrayAccess, ScrubbedBytes)
-import Data.ByteArray.Sized (ByteArrayN, SizedByteArray)
-import Data.Kind (Constraint)
-import GHC.TypeLits (type (<=), KnownNat)
-import System.IO.Unsafe (unsafePerformIO)
-
-import qualified Libsodium as Na
-
-import Crypto.Key.Internal (DerivationSlip, Params (..))
-
-import qualified Crypto.Key.Internal as I
-import qualified Crypto.Random
-
-
--- | “At least as secure as”.
---
--- @a !>=! b@ means that the storage behind a is not less secure than b.
--- This is a little bit of an ad-hoc safety hack, which ensures that if
--- @b@ is stored in a securely allocated memory, then @a@ is stored in
--- memory allocated as securely, or more securely.
---
--- Here are our very ad-hoc rules:
---
--- * This relation is reflexive (@a@ is as secure as @a@ for any @a@).
--- * 'ScrubbedBytes' is more secure than anything.
--- * Everything else is equally (in)secure.
---
--- So, for example, if the original password is stored in @ScrubbedBytes@,
--- you will not be able to put the derived from it key into a @ByteString@,
--- because that would be less secure.
-type family a !>=! b :: Constraint where
-  a !>=! a = ()  -- reflexivity
-  a !>=! ScrubbedBytes = LessSecureStorage a ScrubbedBytes
-  a !>=! b = ()
-class LessSecureStorage a b
-
-
--- | Derive a key from a password using a secure KDF for the first time.
---
--- This function takes two arguments:
---
--- 1. key derivation parameters, which specify how slow the derivation process
--- will be (the slower you can afford the better for security),
---
--- 2. the user’s password to derive the key from.
---
--- See @libsodium@ documentation for how to determine 'Params'.
---
--- It returns the derived key and a /slip/ that you need to save in order to be
--- able to derive the same key from the same password in the future. The slip
--- is not secret, so you can store it in plaintext; just make sure you can
--- access it in the future, as you will need to provide it to 'rederive'.
---
--- It can derive a key of almost any length and the output length is encoded
--- in the type. There is an additional type-level restriction which forces
--- you to store the derived key in memory at least as securely as you
--- stored the password.
---
--- Note: This function is not thread-safe until Sodium is initialised.
--- See "Crypto.Init" for details.
-derive
-  ::  forall key n passwd.
-      ( ByteArrayAccess passwd
-      , ByteArrayN n key, key !>=! passwd
-      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX
-      )
-  => I.Params -- ^ Derivation parameters.
-  -> passwd  -- ^ Password to derive from.
-  -> IO (Maybe (key, I.DerivationSlip))
-derive = I.derive
-
--- | Reerive a key from a password using a secure KDF.
---
--- This function takes two arguments:
---
--- 1. A derivation slip previously returned by 'derive'.
---
--- 2. The user’s password.
---
--- This function is guaranteed to derive the same key from the same password
--- as long as the same derivation slip was provided.
---
--- See 'derive' for additional details.
-rederive
-  ::  forall key n passwd.
-      ( ByteArrayAccess passwd
-      , ByteArrayN n key, key !>=! passwd
-      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX
-      )
-  => I.DerivationSlip -- ^ Original derivation slip.
-  -> passwd  -- ^ Password to rederive from.
-  -> Maybe key
-rederive slip passwd =
-  unsafePerformIO $ I.rederive slip passwd
-  -- This IO is safe, because it is pure.
-
-
--- | Generate a new secret key using a cryptographically-secure generator.
---
--- This is just a specialisation of @Crypto.Random.'generate'@ that stores
--- it in a secure memory location.
---
--- Note: This function is not thread-safe until Sodium is initialised.
--- See "Crypto.Init" for details.
-generate :: KnownNat n => IO (SizedByteArray n ScrubbedBytes)
-generate = Crypto.Random.generate
diff --git a/lib/Crypto/Key/Internal.hs b/lib/Crypto/Key/Internal.hs
deleted file mode 100644
--- a/lib/Crypto/Key/Internal.hs
+++ /dev/null
@@ -1,106 +0,0 @@
-{-# LANGUAGE TupleSections #-}
-{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE TypeOperators #-}
-
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
--- | Key derivation/generation internals.
-module Crypto.Key.Internal
-  ( Params (..)
-  , DerivationSlip
-  , derive
-  , rederive
-
-  , DerivationSlipData (..)
-  , derivationSlipEncode
-  , derivationSlipDecode
-  ) where
-
-import Control.Monad (when)
-import Data.ByteArray (ByteArrayAccess)
-import Data.ByteArray.Sized (ByteArrayN, sizedByteArray, unSizedByteArray)
-import Data.ByteString (ByteString)
-import Data.Serialize (Serialize (put, get), decode, encode)
-import Data.Word (Word8)
-import GHC.TypeLits (type (<=))
-
-import qualified Libsodium as Na
-
-import Crypto.Nonce (generate)
-import Crypto.Pwhash.Internal (Algorithm (Argon2id_1_3), Params (..), Salt, pwhash)
-
-
--- | Opaque bytes that contain the salt and pwhash params.
-type DerivationSlip = ByteString
-
--- | Data contained in a derivation slip.
---
--- This data type is used only internally within this module for
--- convenience. It is exported only for testing purposes.
---
--- Currently only one KDF is supported, so it is assumed implicitly,
--- however the actual binary encoding contains an identifier of the KDF
--- used (for forward-compatibility).
-data DerivationSlipData = DerivationSlipData
-  { params :: !Params
-  , salt :: !(Salt ByteString)
-  }
-  deriving (Eq, Show)
-
-instance Serialize DerivationSlipData where
-  put (DerivationSlipData Params{opsLimit, memLimit} salt) = do
-    put (1 :: Word8)  -- algorithm marker for forward-compatibility
-    put opsLimit >> put memLimit
-    put (unSizedByteArray salt)
-  get = do
-    tag <- get @Word8
-    when (tag /= 1) $ fail "Wrong algorithm parameters encoding tag"
-    params <- Params <$> get <*> get
-    msalt <- sizedByteArray <$> get @ByteString
-    case msalt of
-      Nothing -> fail "Unexpected salt size"
-      Just salt -> pure $ DerivationSlipData params salt
-
-
--- | Encode derivation slip data into bytes.
-derivationSlipEncode :: DerivationSlipData -> DerivationSlip
-derivationSlipEncode = encode
-
--- | Decode derivation slip data from bytes.
-derivationSlipDecode :: DerivationSlip -> Maybe DerivationSlipData
-derivationSlipDecode bytes = case decode bytes of
-  Right slip -> Just slip
-  Left _ -> Nothing
-
-
--- | Derive a key for the first time.
-derive
-  ::  ( ByteArrayAccess passwd
-      , ByteArrayN n key
-      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX
-      )
-  => Params
-  -> passwd
-  -> IO (Maybe (key, DerivationSlip))
-derive params passwd = do
-  salt <- generate
-  mkey <- pwhash Argon2id_1_3 params passwd salt
-  let slip = DerivationSlipData params salt
-  pure $ fmap (, derivationSlipEncode slip) mkey
-
--- | Derive the same key form the same password again.
-rederive
-  ::  ( ByteArrayAccess passwd
-      , ByteArrayN n key
-      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX
-      )
-  => DerivationSlip
-  -> passwd
-  -> IO (Maybe key)
-rederive slip passwd =
-  case derivationSlipDecode slip of
-    Nothing -> pure Nothing
-    Just (DerivationSlipData{params, salt}) ->
-      pwhash Argon2id_1_3 params passwd salt
diff --git a/lib/Crypto/Mac.hs b/lib/Crypto/Mac.hs
deleted file mode 100644
--- a/lib/Crypto/Mac.hs
+++ /dev/null
@@ -1,69 +0,0 @@
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
-{-# OPTIONS_HADDOCK not-home #-}
-
--- ! This module merely re-exports definitions from the corresponding
--- ! module in NaCl and alters the Haddock to make it more specific
--- ! to crypto-sodium. So, the docs should be kept more-or-less in sync.
-
--- | Message authentication codes.
---
--- It is best to import this module qualified:
---
--- @
--- import qualified Crypto.Mac as Mac
---
--- authenticator = Mac.'create' key message
--- if Mac.'verify' key message authenticator
--- then {- Ok! -}
--- else {- Fail! -}
--- @
---
--- A message authenticator is like a signature, except that the key is
--- secret. It can be used when it is not necessary to encrypt the data,
--- but its integrity needs to be guaranteed.
-module Crypto.Mac
-  (
-  -- * Keys
-    Key
-  , toKey
-
-  -- * Authenticator tags
-  , Authenticator
-  , toAuthenticator
-
-  -- * Authentication
-  , create
-  , verify
-  ) where
-
-import NaCl.Auth (Authenticator, Key, toAuthenticator, toKey, verify)
-import Data.ByteArray (ByteArray, ByteArrayAccess)
-
-import qualified NaCl.Auth as NaCl.Auth
-
-
--- | Create an authenticator for a message.
---
--- @
--- authenticator = Mac.create key message
--- @
---
--- *   @key@ is the secret key used for authentication. See "Crypto.Key" for how
---     to get one.
---
--- *   @message@ is the data you are authenticating.
---
--- This function produces authentication data, so if anyone modifies the message,
--- @verify@ will return @False@.
-create
-  ::  ( ByteArray authBytes
-      , ByteArrayAccess keyBytes
-      , ByteArrayAccess msg
-      )
-  => Key keyBytes  -- ^ Secret key.
-  -> msg  -- ^ Message to authenticate.
-  -> Authenticator authBytes
-create = NaCl.Auth.create
diff --git a/lib/Crypto/Nonce.hs b/lib/Crypto/Nonce.hs
deleted file mode 100644
--- a/lib/Crypto/Nonce.hs
+++ /dev/null
@@ -1,46 +0,0 @@
-{-# OPTIONS_GHC -Wno-redundant-constraints #-}
-{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE TypeOperators #-}
-
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
--- | This module gives different ways of obtaining nonces.
---
--- A “nonce” is additional input provided to an encryption algorithm.
--- The most important rule is that you cannot use the same nonce to encrypt
--- more than one message. What will happen if you reuse a nonce depends
--- on the details of the cryptographic algorithm, but, in general, expect
--- the cryptography to fail completely in this case.
---
--- = Random nonce generation
---
--- The easiest way to guarantee that nonces do not repeat is to use a
--- random nonce every time. If the nonce is large enough, then the
--- probably of a random nonce repeating will be negligible.
--- All cryptographic functions provided by this library can be
--- safely used with random nonces.
---
--- The 'generate' function is great at generating new random nonces.
--- The nonces are produced using the standard @random@ package, so
--- the generation is very fast.
-module Crypto.Nonce
-  (
-  -- * Random nonce generation
-    generate
-  ) where
-
-import Data.ByteArray.Sized (SizedByteArray)
-import Data.ByteString (ByteString)
-import GHC.TypeLits (KnownNat)
-
-import Crypto.Internal.Random (generateInsecure)
-
-
--- | Generate a new random nonce.
---
--- Note: random data generated by the functions in this module is only suitable
--- to be used as a nonce, but never as a secret key.
-generate :: KnownNat n => IO (SizedByteArray n ByteString)
-generate = generateInsecure
diff --git a/lib/Crypto/Pwhash/Internal.hs b/lib/Crypto/Pwhash/Internal.hs
deleted file mode 100644
--- a/lib/Crypto/Pwhash/Internal.hs
+++ /dev/null
@@ -1,82 +0,0 @@
-{-# OPTIONS_GHC -Wno-redundant-constraints #-}
-{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE TypeOperators #-}
-
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
--- | Tools for hashing passwords.
-module Crypto.Pwhash.Internal
-  ( Algorithm (..)
-  , Params (..)
-  , Salt
-
-  , pwhash
-  ) where
-
-import Prelude hiding (length)
-
-import Data.ByteArray (ByteArrayAccess, length, withByteArray)
-import Data.ByteArray.Sized (ByteArrayN, SizedByteArray, allocRet)
-import Data.Proxy (Proxy (Proxy))
-import Data.Word (Word64)
-import GHC.TypeLits (type (<=), natVal)
-import Foreign.C.Types (CInt, CSize (CSize), CULLong (CULLong))
-
-import qualified Libsodium as Na
-
-
--- | Secure hashing algorithm.
-data Algorithm
-  = Argon2i_1_3 -- ^ Argon2i version 1.3
-  | Argon2id_1_3 -- ^ Argon2id version 1.3
-  deriving (Eq, Ord, Show)
-
-algorithmToInt :: Algorithm -> CInt
-algorithmToInt Argon2i_1_3 = Na.crypto_pwhash_alg_argon2i13
-algorithmToInt Argon2id_1_3 = Na.crypto_pwhash_alg_argon2id13
-
-
--- | Secure-hashing parameters.
-data Params = Params
-  { opsLimit :: !Word64  -- ^ Maximum amount of computation to perform.
-  , memLimit :: !Word64  -- ^ Maximum amount of RAM (bytes) to use.
-  }
-  deriving (Eq, Ord, Show)
-
-
--- | Salt used for password hashing.
---
--- This type is parametrised by the actual data type that contains
--- bytes. This can be, for example, a @ByteString@.
-type Salt a = SizedByteArray Na.CRYPTO_PWHASH_SALTBYTES a
-
-
--- | Securely hash a password.
---
--- This is @crypto_pwhash@, it can be used for key derivation.
-pwhash
-  ::  forall passwd salt n hash.
-      ( ByteArrayAccess passwd, ByteArrayAccess salt
-      , ByteArrayN n hash
-      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX
-      )
-  => Algorithm  -- ^ Hashing algorithm.
-  -> Params  -- ^ Hashing parameters.
-  -> passwd  -- ^ Password to hash.
-  -> Salt salt  -- ^ Hashing salt.
-  -> IO (Maybe hash)
-pwhash alg Params{opsLimit, memLimit} passwd salt = do
-  (ret, hash) <-
-    allocRet (Proxy :: Proxy n) $ \hashPtr ->
-    withByteArray passwd $ \passwdPtr ->
-    withByteArray salt $ \saltPtr -> do
-      Na.crypto_pwhash hashPtr (fromIntegral $ natVal (Proxy :: Proxy n))
-        passwdPtr (fromIntegral $ length passwd)
-        saltPtr
-        (CULLong opsLimit) (CSize memLimit) (algorithmToInt alg)
-  if ret == 0 then
-    pure $ Just hash
-  else
-    pure $ Nothing
diff --git a/lib/Crypto/Random.hs b/lib/Crypto/Random.hs
deleted file mode 100644
--- a/lib/Crypto/Random.hs
+++ /dev/null
@@ -1,30 +0,0 @@
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
--- | Generate cryptographically-secure random data.
-module Crypto.Random
-  ( generate
-  ) where
-
-import Data.ByteArray (ByteArray)
-import Data.ByteArray.Sized (SizedByteArray, alloc)
-import Data.Proxy (Proxy (Proxy))
-import GHC.TypeLits (KnownNat, natVal)
-
-import qualified Libsodium as Na
-
-
--- | Generate a sequence of cryptographically-secure random bytes.
---
--- The output of this function is suitable to generate secret keys.
---
--- Note: This function is not thread-safe until Sodium is initialised.
--- See "Crypto.Init" for details.
-generate
-  :: forall ba n. (ByteArray ba, KnownNat n)
-  => IO (SizedByteArray n ba)
-generate = alloc $ \bytesPtr ->
-    Na.randombytes_buf bytesPtr len
-  where
-    len = fromIntegral $ natVal (Proxy :: Proxy n)
diff --git a/lib/Crypto/Sign.hs b/lib/Crypto/Sign.hs
deleted file mode 100644
--- a/lib/Crypto/Sign.hs
+++ /dev/null
@@ -1,87 +0,0 @@
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
-{-# OPTIONS_HADDOCK not-home #-}
-
--- ! This module merely re-exports definitions from the corresponding
--- ! module in NaCl and alters the Haddock to make it more specific
--- ! to crypto-sodium. So, the docs should be kept more-or-less in sync.
-
--- | Public-key signatures.
---
--- It is best to import this module qualified:
---
--- @
--- import qualified Crypto.Sign as Sign
---
--- signed = Sign.'create' sk message
--- verified = Sign.'open' pk signed
--- @
---
--- Functions in this modules work with /combined/ signatures.
--- This means that when you sign a message, it will be copied as is
--- and then a signature will be prepended. So you should treat the
--- resulting value as a transparent (because it is not encrypted)
--- package with a signature attached on top.
---
--- Instead of accessing the message directly, you should use
--- 'open', which will verify the signature and return a copy of the
--- original message only if the signature was valid.
-module Crypto.Sign
-  (
-  -- * Keys
-    PublicKey
-  , toPublicKey
-  , SecretKey
-  , toSecretKey
-  , keypair
-  , keypairFromSeed
-  , unsafeKeypairFromSeed
-
-  -- * Signing/verifying
-  , create
-  , open
-  ) where
-
-import Data.ByteArray (ByteArrayAccess, ScrubbedBytes, withByteArray)
-import Data.ByteString (ByteString)
-import Data.ByteArray.Sized (SizedByteArray, alloc, allocRet)
-import Data.Functor (void)
-import Data.Proxy (Proxy(..))
-import System.IO.Unsafe (unsafePerformIO)
-
-import qualified Libsodium as Na
-
-import NaCl.Sign
-  (PublicKey, SecretKey, create, keypair, open, toPublicKey, toSecretKey)
-
--- | Seed for deterministically generating a keypair.
---
--- In accordance with Libsodium's documentation, the seed must be of size
--- @Na.CRYPTO_SIGN_SEEDBYTES@.
---
--- This type is parametrised by the actual data type that contains
--- bytes. This can be, for example, a @ByteString@.
-type Seed a = SizedByteArray Na.CRYPTO_SIGN_SEEDBYTES a
-
-
--- | Generate a new 'SecretKey' together with its 'PublicKey' from a given seed.
-keypairFromSeed
-  :: ByteArrayAccess seed
-  => Seed seed
-  -> IO (PublicKey ByteString, SecretKey ScrubbedBytes)
-keypairFromSeed seed = do
-  allocRet Proxy $ \skPtr ->
-    alloc $ \pkPtr ->
-    withByteArray seed $ \sdPtr ->
-    -- always returns 0, so we don’t check it
-    void $ Na.crypto_sign_seed_keypair pkPtr skPtr sdPtr
-
--- | Generate a new 'SecretKey' together with its 'PublicKey' from a given seed,
--- in a pure context.
-unsafeKeypairFromSeed
-  :: ByteArrayAccess seed
-  => Seed seed
-  -> (PublicKey ByteString, SecretKey ScrubbedBytes)
-unsafeKeypairFromSeed = unsafePerformIO . keypairFromSeed
diff --git a/lib/Crypto/Sodium/Encrypt/Public.hs b/lib/Crypto/Sodium/Encrypt/Public.hs
new file mode 100644
--- /dev/null
+++ b/lib/Crypto/Sodium/Encrypt/Public.hs
@@ -0,0 +1,154 @@
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+{-# OPTIONS_HADDOCK not-home #-}
+
+-- ! This module merely re-exports definitions from the corresponding
+-- ! module in NaCl and alters the Haddock to make it more specific
+-- ! to crypto-sodium. So, the docs should be kept more-or-less in sync.
+
+-- | Public-key authenticated encryption.
+--
+-- It is best to import this module qualified:
+--
+-- @
+-- import qualified Crypto.Sodium.Encrypt.Public as Public
+--
+-- encrypted = Public.'encrypt' pk sk nonce message
+-- decrypted = Public.'decrypt' pk sk nonce encrypted
+-- @
+--
+-- A box is an abstraction from NaCl. One way to think about it
+-- is to imagine that you are putting data into a box protected by
+-- the receiver’s public key and signed by your private key. The
+-- receive will then be able to 'open' it using their private key
+-- and your public key.
+--
+-- Note that this means that you need to exchange your public keys
+-- in advance. It might seem strange at first that the receiver
+-- needs to know your public key too, but this is actually very important
+-- as otherwise the receiver would not be able to have any guarantees
+-- regarding the source or the integrity of the data.
+module Crypto.Sodium.Encrypt.Public
+  (
+  -- * Keys
+    PublicKey
+  , toPublicKey
+  , SecretKey
+  , toSecretKey
+  , keypair
+  , keypairFromSeed
+  , unsafeKeypairFromSeed
+
+  -- * Nonce
+  , Nonce
+  , toNonce
+
+  -- * Encryption/decryption
+  , encrypt
+  , decrypt
+  ) where
+
+import Data.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes, withByteArray)
+import Data.ByteArray.Sized as Sized (SizedByteArray, alloc, allocRet)
+import Data.ByteString (ByteString)
+import Data.Functor (void)
+import Data.Proxy (Proxy(..))
+import System.IO.Unsafe (unsafePerformIO)
+
+import qualified Libsodium as Na
+
+import NaCl.Box
+  (Nonce, PublicKey, SecretKey, keypair, toNonce, toPublicKey, toSecretKey)
+import qualified NaCl.Box as NaCl.Box
+
+
+-- | Encrypt a message.
+--
+-- @
+-- encrypted = Public.encrypt pk sk nonce message
+-- @
+--
+-- *   @pk@ is the receiver’s public key, used for encryption.
+--     @sk@ is the sender’s secret key, used for authentication.
+--
+--     These are generated using 'keypair' and are supposed to be exchanged
+--     in advance. Both parties need to know their own secret key and the other’s
+--     public key.
+--
+-- *   @nonce@ is an extra noise that ensures that is required for security.
+--     See "Crypto.Sodium.Nonce" for how to work with it.
+--
+-- *   @message@ is the data you are encrypting.
+--
+-- This function adds authentication data, so if anyone modifies the cyphertext,
+-- 'decrypt' will refuse to decrypt it.
+encrypt
+  ::  ( ByteArrayAccess pkBytes, ByteArrayAccess skBytes
+      , ByteArrayAccess nonceBytes
+      , ByteArrayAccess ptBytes, ByteArray ctBytes
+      )
+  => PublicKey pkBytes  -- ^ Receiver’s public key
+  -> SecretKey skBytes  -- ^ Sender’s secret key
+  -> Nonce nonceBytes  -- ^ Nonce
+  -> ptBytes -- ^ Plaintext message
+  -> ctBytes
+encrypt = NaCl.Box.create
+
+
+-- | Decrypt a message.
+--
+-- @
+-- decrypted = Public.decrypt sk pk nonce encrypted
+-- @
+--
+-- * @sk@ is the receiver’s secret key, used for decription.
+-- * @pk@ is the sender’s public key, used for authentication.
+-- * @nonce@ is the same that was used for encryption.
+-- * @encrypted@ is the output of 'encrypt'.
+--
+-- This function will return @Nothing@ if the encrypted message was tampered
+-- with after it was encrypted.
+decrypt
+  ::  ( ByteArrayAccess skBytes, ByteArrayAccess pkBytes
+      , ByteArrayAccess nonceBytes
+      , ByteArray ptBytes, ByteArrayAccess ctBytes
+      )
+  => SecretKey skBytes  -- ^ Receiver’s secret key
+  -> PublicKey pkBytes  -- ^ Sender’s public key
+  -> Nonce nonceBytes  -- ^ Nonce
+  -> ctBytes -- ^ Encrypted message (cyphertext)
+  -> Maybe ptBytes
+decrypt = NaCl.Box.open
+
+
+-- | Seed for deterministically generating a keypair.
+--
+-- In accordance with Libsodium's documentation, the seed must be of size
+-- @Na.CRYPTO_BOX_SEEDBYTES@.
+--
+-- This type is parametrised by the actual data type that contains
+-- bytes. This can be, for example, a @ByteString@.
+type Seed a = SizedByteArray Na.CRYPTO_BOX_SEEDBYTES a
+
+
+-- | Generate a new 'SecretKey' together with its 'PublicKey' from a given seed.
+keypairFromSeed
+  :: ByteArrayAccess seed
+  => Seed seed
+  -> IO (PublicKey ByteString, SecretKey ScrubbedBytes)
+keypairFromSeed seed = do
+  allocRet Proxy $ \skPtr ->
+    alloc $ \pkPtr ->
+    withByteArray seed $ \sdPtr ->
+    -- always returns 0, so we don’t check it
+    void $ Na.crypto_box_seed_keypair pkPtr skPtr sdPtr
+
+-- | Generate a new 'SecretKey' together with its 'PublicKey' from a given seed,
+-- in a pure context.
+unsafeKeypairFromSeed
+  :: ByteArrayAccess seed
+  => Seed seed
+  -> (PublicKey ByteString, SecretKey ScrubbedBytes)
+unsafeKeypairFromSeed = unsafePerformIO . keypairFromSeed
diff --git a/lib/Crypto/Sodium/Encrypt/Symmetric.hs b/lib/Crypto/Sodium/Encrypt/Symmetric.hs
new file mode 100644
--- /dev/null
+++ b/lib/Crypto/Sodium/Encrypt/Symmetric.hs
@@ -0,0 +1,94 @@
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+{-# OPTIONS_HADDOCK not-home #-}
+
+-- ! This module merely re-exports definitions from the corresponding
+-- ! module in NaCl and alters the Haddock to make it more specific
+-- ! to crypto-sodium. So, the docs should be kept more-or-less in sync.
+
+-- | Symmetric authenticated encryption.
+--
+-- It is best to import this module qualified:
+--
+-- @
+-- import qualified Crypto.Sodium.Encrypt.Symmetric as Symmetric
+--
+-- encrypted = Symmetric.'encrypt' key nonce message
+-- decrypted = Symmetric.'decrypt' key nonce encrypted
+-- @
+--
+-- In NaCl this is know as a “Secretbox”. One way to think about it
+-- is to imagine that you are putting data into a box protected by a
+-- secret key. You “create” such a box using 'encrypt', store it somewhere
+-- (it is just a sequence of bytes), and when you need it in the
+-- future, you “open” it with 'decrypt' using the same secret key.
+module Crypto.Sodium.Encrypt.Symmetric
+  (
+  -- * Keys
+    Key
+  , toKey
+
+  -- * Nonce
+  , Nonce
+  , toNonce
+
+  -- * Encryption/decryption
+  , encrypt
+  , decrypt
+  ) where
+
+import NaCl.Secretbox (Key, Nonce, toKey, toNonce)
+import Data.ByteArray (ByteArray, ByteArrayAccess)
+
+import qualified NaCl.Secretbox as NaCl.Secretbox
+
+
+-- | Encrypt a message.
+--
+-- @
+-- encrypted = Symmetric.encrypt key nonce message
+-- @
+--
+-- *   @key@ is the secret key used for encryption. See "Crypto.Sodium.Key" for how
+--     to get one.
+--
+-- *   @nonce@ is an extra noise that is required for security.
+--     See "Crypto.Sodium.Nonce" for how to work with it.
+--
+-- *   @message@ is the data you are encrypting.
+--
+-- This function adds authentication data, so if anyone modifies the cyphertext,
+-- 'open' will refuse to decrypt it.
+encrypt
+  ::  ( ByteArrayAccess keyBytes, ByteArrayAccess nonceBytes
+      , ByteArrayAccess ptBytes, ByteArray ctBytes
+      )
+  => Key keyBytes  -- ^ Secret key
+  -> Nonce nonceBytes  -- ^ Nonce
+  -> ptBytes -- ^ Plaintext message
+  -> ctBytes
+encrypt = NaCl.Secretbox.create
+
+
+-- | Decrypt a message.
+--
+-- @
+-- decrypted = Symmetric.decrypt key nonce encrypted
+-- @
+--
+-- * @key@ and @nonce@ are the same that were used for encryption.
+-- * @encrypted@ is the output of 'create'.
+--
+-- This function will return @Nothing@ if the encrypted message was tampered
+-- with after it was encrypted.
+decrypt
+  ::  ( ByteArrayAccess keyBytes, ByteArrayAccess nonceBytes
+      , ByteArray ptBytes, ByteArrayAccess ctBytes
+      )
+  => Key keyBytes  -- ^ Secret key
+  -> Nonce nonceBytes  -- ^ Nonce
+  -> ctBytes -- ^ Encrypted message (cyphertext)
+  -> Maybe ptBytes
+decrypt = NaCl.Secretbox.open
diff --git a/lib/Crypto/Sodium/Encrypt/Symmetric/Stream.hs b/lib/Crypto/Sodium/Encrypt/Symmetric/Stream.hs
new file mode 100644
--- /dev/null
+++ b/lib/Crypto/Sodium/Encrypt/Symmetric/Stream.hs
@@ -0,0 +1,41 @@
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+{-# OPTIONS_HADDOCK not-home #-}
+
+-- | Symmetric authenticated encryption for streams.
+--
+-- This module provides generic types for Sodium-based streaming
+-- encryption. It does not provide any functionality itself.
+--
+-- There are separate packages that actually implement this functionality
+-- for specific streaming libraries:
+--
+-- * @crypto-sodium-streamly@ for @streamly@ streams.
+module Crypto.Sodium.Encrypt.Symmetric.Stream
+  (
+  -- * Keys
+    Key
+  , toKey
+  ) where
+
+import Data.ByteArray (ByteArrayAccess)
+import Data.ByteArray.Sized (SizedByteArray, sizedByteArray)
+
+import qualified Libsodium as Na
+
+
+-- | Encryption key that can be used for streaming symmetric encryption.
+--
+-- This type is parametrised by the actual data type that contains
+-- bytes. This can be, for example, a @ByteString@, but, since this
+-- is a secret key, it is better to use @ScrubbedBytes@.
+type Key a = SizedByteArray Na.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES a
+
+-- | Make a 'Key' from an arbitrary byte array.
+--
+-- This function returns @Just@ if and only if the byte array has
+-- the right length to be used as a key with a streaming symmetric encryption.
+toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)
+toKey = sizedByteArray
diff --git a/lib/Crypto/Sodium/Hash.hs b/lib/Crypto/Sodium/Hash.hs
new file mode 100644
--- /dev/null
+++ b/lib/Crypto/Sodium/Hash.hs
@@ -0,0 +1,87 @@
+-- SPDX-FileCopyrightText: 2021 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+{-# LANGUAGE ExplicitNamespaces, TypeOperators, TypeFamilies #-}
+
+-- | Hashing.
+--
+-- It is best to import this module qualified:
+--
+-- @
+-- import qualified Crypto.Sodium.Hash as Hash
+--
+-- hash_blake2b256_keyed = Hash.'blake2bWithKey' \@32 key message
+-- hash_blake2b256 = Hash.'blake2b' \@32 message
+-- hash_blake2b512 = Hash.'blake2b' \@64 message
+--
+-- hash_sha256 = Hash.'sha256' message
+-- hash_sha512 = Hash.'sha512' message
+-- @
+module Crypto.Sodium.Hash
+  (
+  -- * BLAKE2b
+    I.HashBlake2b
+  , blake2b
+  , blake2bWithKey
+
+  -- * SHA-2
+  , HashSha256
+  , sha256
+
+  , HashSha512
+  , sha512
+  ) where
+
+import Data.ByteArray (ByteArray, ByteArrayAccess, Bytes)
+import GHC.TypeNats (KnownNat, type (<=))
+import NaCl.Hash (HashSha256, HashSha512, sha256, sha512)
+import System.IO.Unsafe (unsafePerformIO)
+
+import qualified Crypto.Sodium.Hash.Internal as I
+import qualified Libsodium as Na
+
+-- | Hash a message using BLAKE2b.
+--
+-- @
+-- hash128 = Hash.'blake2b' \@16 message
+-- hash256 = Hash.'blake2b' \@32 message
+-- hash512 = Hash.'blake2b' \@64 message
+-- @
+--
+-- *   @message@ is the data you are hashing.
+blake2b
+  ::  forall len hashBytes pt.
+      ( ByteArrayAccess pt
+      , ByteArray hashBytes
+      , KnownNat len
+      , Na.CRYPTO_GENERICHASH_BYTES_MIN <= len
+      , len <= Na.CRYPTO_GENERICHASH_BYTES_MAX
+      )
+  => pt  -- ^ Message to hash
+  -> I.HashBlake2b len hashBytes
+blake2b msg = unsafePerformIO $ I.blake2b (Nothing :: Maybe Bytes) msg
+
+-- | Hash a message using BLAKE2b with a key.
+--
+-- @
+-- hash128_keyed = Hash.'blake2bWithKey' \@16 key message
+-- hash256_keyed = Hash.'blake2bWithKey' \@32 key message
+-- hash512_keyed = Hash.'blake2bWithKey' \@64 key message
+-- @
+--
+-- *   @key@ is the BLAKE2b key.
+-- *   @message@ is the data you are hashing.
+blake2bWithKey
+  ::  forall len hashBytes pt key.
+      ( ByteArrayAccess pt
+      , ByteArrayAccess key
+      , ByteArray hashBytes
+      , KnownNat len
+      , Na.CRYPTO_GENERICHASH_BYTES_MIN <= len
+      , len <= Na.CRYPTO_GENERICHASH_BYTES_MAX
+      )
+  => key -- ^ Hash key
+  -> pt  -- ^ Message to hash
+  -> I.HashBlake2b len hashBytes
+blake2bWithKey key msg = unsafePerformIO $ I.blake2b (Just key) msg
diff --git a/lib/Crypto/Sodium/Hash/Internal.hs b/lib/Crypto/Sodium/Hash/Internal.hs
new file mode 100644
--- /dev/null
+++ b/lib/Crypto/Sodium/Hash/Internal.hs
@@ -0,0 +1,58 @@
+-- SPDX-FileCopyrightText: 2021 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+{-# LANGUAGE ExplicitNamespaces, TypeOperators, TypeFamilies #-}
+{-# OPTIONS_GHC -Wno-redundant-constraints #-}
+
+-- | Internals of @crypto_generichash@.
+module Crypto.Sodium.Hash.Internal
+  ( HashBlake2b
+  , blake2b
+  ) where
+
+import Prelude hiding (length)
+
+import Data.ByteArray (ByteArray, ByteArrayAccess, length, withByteArray)
+import Data.ByteArray.Sized (SizedByteArray, allocRet)
+import Data.Proxy (Proxy (Proxy))
+import Foreign.Ptr (nullPtr)
+import GHC.TypeNats (KnownNat, natVal, type (<=))
+
+import qualified Libsodium as Na
+
+-- | Hash returned by 'blake2b'.
+--
+-- This type is parametrised by hash size in bytes and the actual data type
+-- that contains bytes. This can be, for example, a @ByteString@.
+--
+-- Length must be between 16 and 64 bytes.
+type HashBlake2b len a = SizedByteArray len a
+
+-- | Hash a message using BLAKE2b.
+blake2b
+  ::  forall len hashBytes pt key.
+      ( ByteArrayAccess pt
+      , ByteArrayAccess key
+      , ByteArray hashBytes
+      , KnownNat len
+      , Na.CRYPTO_GENERICHASH_BYTES_MIN <= len
+      , len <= Na.CRYPTO_GENERICHASH_BYTES_MAX
+      )
+  => Maybe key -- ^ Hash key
+  -> pt  -- ^ Message to hash
+  -> IO (HashBlake2b len hashBytes)
+blake2b key msg = do
+  (_ret, hash) <-
+    allocRet @len Proxy $ \hashPtr ->
+    withByteArray msg $ \msgPtr ->
+    withKey $ \keyPtr ->
+      Na.crypto_generichash_blake2b hashPtr (fromIntegral $ natVal @len Proxy)
+        msgPtr (fromIntegral $ length msg)
+        keyPtr keyLen
+  -- _ret can be only 0, so we don’t check it
+  pure hash
+  where
+    (withKey, keyLen)
+      | Just key' <- key = (withByteArray key', fromIntegral $ length key')
+      | otherwise = (($ nullPtr), 0)
diff --git a/lib/Crypto/Sodium/Init.hs b/lib/Crypto/Sodium/Init.hs
new file mode 100644
--- /dev/null
+++ b/lib/Crypto/Sodium/Init.hs
@@ -0,0 +1,72 @@
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+-- | Libsodium initialisation.
+
+-- = Thread-safety #threadSafety#
+--
+-- Some of the Sodium (and NaCl) functions (those that generate random data)
+-- are not thread-safe. All these functions are explicitly marked as such
+-- in their Haddock documentation.
+--
+-- Calling 'sodiumInit' before they are used makes them thread-safe.
+--
+-- = Performance
+--
+-- Sodium contains multiple implementations of the primitives it provides.
+-- There are generic implementations, that are used by default, and
+-- multiple alternatives optimised for various platforms.
+--
+-- 'sodiumInit' will quickly benchmark all available implementations and choose
+-- the best ones for each primitive.
+module Crypto.Sodium.Init
+  ( sodiumInit
+  , SodiumInitException (..)
+  ) where
+
+import Control.Exception (Exception, throwIO)
+import Libsodium (sodium_init)
+
+
+-- | Initialise libsodium.
+--
+-- This is just @sodium_init()@ from libsodium. Calling it before using
+-- any Sodium functions is optional, but strongly recommended.
+--
+-- This function does the following:
+--
+-- 1. Open @\/dev\/urandom@ (on Unix) to make it accessible even after @chroot()@.
+--
+-- 2. Make all libsodium functions thread-safe.
+--
+-- 3. Benchmark different implementations of cryptographic primitives provided
+-- and choose the best ones.
+--
+-- This function itself is thread-safe (since libsodium-1.0.11).
+sodiumInit :: IO ()
+sodiumInit = sodium_init >>= \case
+  0 ->
+    -- Success!
+    pure ()
+  1 ->
+    -- Already initialised, that’s ok.
+    pure ()
+  _ ->
+    -- If initialisation fails, using libsodium is unsafe, and there is
+    -- really nothing that can be done at this point and there is no way
+    -- to recover.
+    -- It would be nice to provide some helpful diagnostic here, but,
+    -- unfortunately, libsodium gives no information on the failure reason.
+    throwIO SodiumInitFailed
+
+
+-- | Exception thrown by 'sodiumInit'.
+data SodiumInitException
+  = SodiumInitFailed  -- ^ libsodium failed to initialise.
+
+instance Show SodiumInitException where
+  show SodiumInitFailed =
+    "libsodium failed to initialise and is not safe to use"
+
+instance Exception SodiumInitException
diff --git a/lib/Crypto/Sodium/Internal/Random.hs b/lib/Crypto/Sodium/Internal/Random.hs
new file mode 100644
--- /dev/null
+++ b/lib/Crypto/Sodium/Internal/Random.hs
@@ -0,0 +1,31 @@
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+-- | Generate simple insecure random data.
+module Crypto.Sodium.Internal.Random
+  ( generateInsecure
+  ) where
+
+import Data.ByteArray.Sized (SizedByteArray)
+import Data.ByteString (ByteString)
+import GHC.TypeLits (KnownNat)
+
+import Crypto.Sodium.Random (generate)
+
+
+-- | Generate a sequence of random bytes.
+--
+-- The output of this function is NOT suitable for secret keys.
+generateInsecure
+  :: forall n. (KnownNat n)
+  => IO (SizedByteArray n ByteString)
+generateInsecure
+{-
+  = unsafeSizedByteArray . BS.pack . take len . randoms <$> newStdGen
+  where
+    len = fromIntegral $ natVal (Proxy :: Proxy n)
+-}
+  -- Haddock above is actually a lie. We use the same random generator
+  -- as for keys, because, after benchmarking, it happens to be faster :/.
+  = generate
diff --git a/lib/Crypto/Sodium/Internal/Verify.hs b/lib/Crypto/Sodium/Internal/Verify.hs
new file mode 100644
--- /dev/null
+++ b/lib/Crypto/Sodium/Internal/Verify.hs
@@ -0,0 +1,35 @@
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+-- | @crypto_verify_*@
+module Crypto.Sodium.Internal.Verify
+  ( verifyBytes32
+  ) where
+
+import Data.ByteArray (ByteArrayAccess, withByteArray)
+import Data.ByteArray.Sized (SizedByteArray)
+
+import qualified Libsodium as Na
+
+
+-- | Compare two byte arrays of length 32.
+verifyBytes32
+  :: ( ByteArrayAccess ba1
+     , ByteArrayAccess ba2
+     )
+  => SizedByteArray 32 ba1  -- ^ First byte array
+  -> SizedByteArray 32 ba2  -- ^ Second byte array
+  -> IO Bool
+verifyBytes32 bytes1 bytes2 =
+  withByteArray bytes1 $ \ptr1 ->
+  withByteArray bytes2 $ \ptr2 -> do
+    -- TODO: I have no idea what I am doing
+    --
+    -- - Sodium also checks that pointers are different?
+    -- - This kind of double comparison was added in
+    --   c5a9d46386f917aa0ff1bfb711450f9af1d79a17
+    --   (why?)
+    res1 <- Na.crypto_verify_32 ptr1 ptr2
+    res2 <- Na.sodium_memcmp ptr2 ptr1 32
+    pure $ res1 == 0 && res2 == 0
diff --git a/lib/Crypto/Sodium/Key.hs b/lib/Crypto/Sodium/Key.hs
new file mode 100644
--- /dev/null
+++ b/lib/Crypto/Sodium/Key.hs
@@ -0,0 +1,198 @@
+{-# OPTIONS_GHC -Wno-redundant-constraints #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+-- | This module gives different ways of obtaining secret keys.
+--
+-- = Key derivation (from a password)
+--
+-- Sometimes, instead of generating fresh random transient encryption keys,
+-- you want an encryption key to be persistent and you don’t want to
+-- store it anywhere – instead you ask the user to provide it.
+-- Such a secret value known and entered by the user is usually called
+-- a “password”.
+--
+-- However, passwords make terrible encryption keys because encryption keys:
+--
+-- 1. often need to have a specific exact length and
+-- 2. need to be hard to guess (or brute-force).
+--
+-- Item 1 above can be easily ticked off by deriving the encryption key from
+-- the password by applying a hash-function to it, however in order to
+-- achieve 2 we need our “hash-function” to:
+--
+-- * be slow to compute (to make brute-forcing less feasible) and
+-- * mix extra “noise” into the derivation process to make it harder
+--   to pre-compute derived values in advance.
+--
+-- A construction that satisfies both requirements is called a
+-- /key derivation function (KDF)/.
+-- This module provides a convenient interface for deriving
+-- secure keys from passwords by the way of one such KDF.
+--
+-- == Use
+--
+-- This module provides two functions: 'derive' and 'rederive'.
+-- You can think of the entire process similar to how you set the password
+-- on your account at some website once, and then use this password to log in.
+--
+-- When you derive a key for the first time (e.g. you ask the user to enter their
+-- password twice, and then encrypt something), you use the 'derive' function,
+-- which gives you the derived key and a /derivation slip/. The slip is not
+-- secret, you can store it in plain text and, in fact, you /have to/ store it
+-- in plaintext somewhere next to the encrypted data.
+--
+-- When you need to derive the key in the future (e.g. to decrypt some previously
+-- encrypted data), you will need the user’s password (ask them) /and/ you
+-- will need the original derivation slip, which you should have stored.
+-- You pass these to 'rederive' and it will give you the same key.
+--
+-- @
+-- import qualified Crypto.Sodium.Key as Key
+--
+-- encrypt = do
+--   password <- {- ask the user to enter their password -}
+--   password2 <- {- ask the user to confirm their password -}
+--   when (password /= password2 then) $ throwIO {- passwords do not match -}
+--
+--   let params = {- choose key derivation parameters -}
+--   (key, slip) <- Key.derive params password
+--
+--   {- store slip (it is not secret) -}
+--   {- encrypt data with key -}
+--
+-- decrypt = do
+--   password <- {- ask the user to enter their password -}
+--   slip <- {- get the stored slip -}
+--
+--   key <- Key.rederive slip password
+--
+--   {- decrypt data with key -}
+-- @
+--
+-- = Random key generation
+--
+-- The 'generate' function is great at generating new secure secret keys.
+module Crypto.Sodium.Key
+  ( type (!>=!)
+
+  -- * Key derivation
+  , Params (..)
+  , DerivationSlip
+  , derive
+  , rederive
+
+  -- * Random key generation
+  , generate
+  ) where
+
+import Data.ByteArray (ByteArrayAccess, ScrubbedBytes)
+import Data.ByteArray.Sized (ByteArrayN, SizedByteArray)
+import Data.Kind (Constraint)
+import GHC.TypeLits (type (<=), KnownNat)
+import System.IO.Unsafe (unsafePerformIO)
+
+import qualified Libsodium as Na
+
+import Crypto.Sodium.Key.Internal (DerivationSlip, Params (..))
+
+import qualified Crypto.Sodium.Key.Internal as I
+import qualified Crypto.Sodium.Random
+
+
+-- | “At least as secure as”.
+--
+-- @a !>=! b@ means that the storage behind a is not less secure than b.
+-- This is a little bit of an ad-hoc safety hack, which ensures that if
+-- @b@ is stored in a securely allocated memory, then @a@ is stored in
+-- memory allocated as securely, or more securely.
+--
+-- Here are our very ad-hoc rules:
+--
+-- * This relation is reflexive (@a@ is as secure as @a@ for any @a@).
+-- * 'ScrubbedBytes' is more secure than anything.
+-- * Everything else is equally (in)secure.
+--
+-- So, for example, if the original password is stored in @ScrubbedBytes@,
+-- you will not be able to put the derived from it key into a @ByteString@,
+-- because that would be less secure.
+type family a !>=! b :: Constraint where
+  a !>=! a = ()  -- reflexivity
+  a !>=! ScrubbedBytes = LessSecureStorage a ScrubbedBytes
+  a !>=! b = ()
+class LessSecureStorage a b
+
+
+-- | Derive a key from a password using a secure KDF for the first time.
+--
+-- This function takes two arguments:
+--
+-- 1. key derivation parameters, which specify how slow the derivation process
+-- will be (the slower you can afford the better for security),
+--
+-- 2. the user’s password to derive the key from.
+--
+-- See @libsodium@ documentation for how to determine 'Params'.
+--
+-- It returns the derived key and a /slip/ that you need to save in order to be
+-- able to derive the same key from the same password in the future. The slip
+-- is not secret, so you can store it in plaintext; just make sure you can
+-- access it in the future, as you will need to provide it to 'rederive'.
+--
+-- It can derive a key of almost any length and the output length is encoded
+-- in the type. There is an additional type-level restriction which forces
+-- you to store the derived key in memory at least as securely as you
+-- stored the password.
+--
+-- Note: This function is not thread-safe until Sodium is initialised.
+-- See "Crypto.Sodium.Init" for details.
+derive
+  ::  forall key n passwd.
+      ( ByteArrayAccess passwd
+      , ByteArrayN n key, key !>=! passwd
+      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX
+      )
+  => I.Params -- ^ Derivation parameters.
+  -> passwd  -- ^ Password to derive from.
+  -> IO (Maybe (key, I.DerivationSlip))
+derive = I.derive
+
+-- | Reerive a key from a password using a secure KDF.
+--
+-- This function takes two arguments:
+--
+-- 1. A derivation slip previously returned by 'derive'.
+--
+-- 2. The user’s password.
+--
+-- This function is guaranteed to derive the same key from the same password
+-- as long as the same derivation slip was provided.
+--
+-- See 'derive' for additional details.
+rederive
+  ::  forall key n passwd.
+      ( ByteArrayAccess passwd
+      , ByteArrayN n key, key !>=! passwd
+      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX
+      )
+  => I.DerivationSlip -- ^ Original derivation slip.
+  -> passwd  -- ^ Password to rederive from.
+  -> Maybe key
+rederive slip passwd =
+  unsafePerformIO $ I.rederive slip passwd
+  -- This IO is safe, because it is pure.
+
+
+-- | Generate a new secret key using a cryptographically-secure generator.
+--
+-- This is just a specialisation of @Crypto.Sodium.Random.'generate'@ that stores
+-- it in a secure memory location.
+--
+-- Note: This function is not thread-safe until Sodium is initialised.
+-- See "Crypto.Sodium.Init" for details.
+generate :: KnownNat n => IO (SizedByteArray n ScrubbedBytes)
+generate = Crypto.Sodium.Random.generate
diff --git a/lib/Crypto/Sodium/Key/Internal.hs b/lib/Crypto/Sodium/Key/Internal.hs
new file mode 100644
--- /dev/null
+++ b/lib/Crypto/Sodium/Key/Internal.hs
@@ -0,0 +1,106 @@
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+-- | Key derivation/generation internals.
+module Crypto.Sodium.Key.Internal
+  ( Params (..)
+  , DerivationSlip
+  , derive
+  , rederive
+
+  , DerivationSlipData (..)
+  , derivationSlipEncode
+  , derivationSlipDecode
+  ) where
+
+import Control.Monad (when)
+import Data.ByteArray (ByteArrayAccess)
+import Data.ByteArray.Sized (ByteArrayN, sizedByteArray, unSizedByteArray)
+import Data.ByteString (ByteString)
+import Data.Serialize (Serialize (put, get), decode, encode)
+import Data.Word (Word8)
+import GHC.TypeLits (type (<=))
+
+import qualified Libsodium as Na
+
+import Crypto.Sodium.Nonce (generate)
+import Crypto.Sodium.Pwhash.Internal (Algorithm (Argon2id_1_3), Params (..), Salt, pwhash)
+
+
+-- | Opaque bytes that contain the salt and pwhash params.
+type DerivationSlip = ByteString
+
+-- | Data contained in a derivation slip.
+--
+-- This data type is used only internally within this module for
+-- convenience. It is exported only for testing purposes.
+--
+-- Currently only one KDF is supported, so it is assumed implicitly,
+-- however the actual binary encoding contains an identifier of the KDF
+-- used (for forward-compatibility).
+data DerivationSlipData = DerivationSlipData
+  { params :: !Params
+  , salt :: !(Salt ByteString)
+  }
+  deriving (Eq, Show)
+
+instance Serialize DerivationSlipData where
+  put (DerivationSlipData Params{opsLimit, memLimit} salt) = do
+    put (1 :: Word8)  -- algorithm marker for forward-compatibility
+    put opsLimit >> put memLimit
+    put (unSizedByteArray salt)
+  get = do
+    tag <- get @Word8
+    when (tag /= 1) $ fail "Wrong algorithm parameters encoding tag"
+    params <- Params <$> get <*> get
+    msalt <- sizedByteArray <$> get @ByteString
+    case msalt of
+      Nothing -> fail "Unexpected salt size"
+      Just salt -> pure $ DerivationSlipData params salt
+
+
+-- | Encode derivation slip data into bytes.
+derivationSlipEncode :: DerivationSlipData -> DerivationSlip
+derivationSlipEncode = encode
+
+-- | Decode derivation slip data from bytes.
+derivationSlipDecode :: DerivationSlip -> Maybe DerivationSlipData
+derivationSlipDecode bytes = case decode bytes of
+  Right slip -> Just slip
+  Left _ -> Nothing
+
+
+-- | Derive a key for the first time.
+derive
+  ::  ( ByteArrayAccess passwd
+      , ByteArrayN n key
+      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX
+      )
+  => Params
+  -> passwd
+  -> IO (Maybe (key, DerivationSlip))
+derive params passwd = do
+  salt <- generate
+  mkey <- pwhash Argon2id_1_3 params passwd salt
+  let slip = DerivationSlipData params salt
+  pure $ fmap (, derivationSlipEncode slip) mkey
+
+-- | Derive the same key form the same password again.
+rederive
+  ::  ( ByteArrayAccess passwd
+      , ByteArrayN n key
+      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX
+      )
+  => DerivationSlip
+  -> passwd
+  -> IO (Maybe key)
+rederive slip passwd =
+  case derivationSlipDecode slip of
+    Nothing -> pure Nothing
+    Just (DerivationSlipData{params, salt}) ->
+      pwhash Argon2id_1_3 params passwd salt
diff --git a/lib/Crypto/Sodium/Mac.hs b/lib/Crypto/Sodium/Mac.hs
new file mode 100644
--- /dev/null
+++ b/lib/Crypto/Sodium/Mac.hs
@@ -0,0 +1,69 @@
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+{-# OPTIONS_HADDOCK not-home #-}
+
+-- ! This module merely re-exports definitions from the corresponding
+-- ! module in NaCl and alters the Haddock to make it more specific
+-- ! to crypto-sodium. So, the docs should be kept more-or-less in sync.
+
+-- | Message authentication codes.
+--
+-- It is best to import this module qualified:
+--
+-- @
+-- import qualified Crypto.Sodium.Mac as Mac
+--
+-- authenticator = Mac.'create' key message
+-- if Mac.'verify' key message authenticator
+-- then {- Ok! -}
+-- else {- Fail! -}
+-- @
+--
+-- A message authenticator is like a signature, except that the key is
+-- secret. It can be used when it is not necessary to encrypt the data,
+-- but its integrity needs to be guaranteed.
+module Crypto.Sodium.Mac
+  (
+  -- * Keys
+    Key
+  , toKey
+
+  -- * Authenticator tags
+  , Authenticator
+  , toAuthenticator
+
+  -- * Authentication
+  , create
+  , verify
+  ) where
+
+import NaCl.Auth (Authenticator, Key, toAuthenticator, toKey, verify)
+import Data.ByteArray (ByteArray, ByteArrayAccess)
+
+import qualified NaCl.Auth as NaCl.Auth
+
+
+-- | Create an authenticator for a message.
+--
+-- @
+-- authenticator = Mac.create key message
+-- @
+--
+-- *   @key@ is the secret key used for authentication. See "Crypto.Sodium.Key" for how
+--     to get one.
+--
+-- *   @message@ is the data you are authenticating.
+--
+-- This function produces authentication data, so if anyone modifies the message,
+-- @verify@ will return @False@.
+create
+  ::  ( ByteArray authBytes
+      , ByteArrayAccess keyBytes
+      , ByteArrayAccess msg
+      )
+  => Key keyBytes  -- ^ Secret key.
+  -> msg  -- ^ Message to authenticate.
+  -> Authenticator authBytes
+create = NaCl.Auth.create
diff --git a/lib/Crypto/Sodium/Nonce.hs b/lib/Crypto/Sodium/Nonce.hs
new file mode 100644
--- /dev/null
+++ b/lib/Crypto/Sodium/Nonce.hs
@@ -0,0 +1,46 @@
+{-# OPTIONS_GHC -Wno-redundant-constraints #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+-- | This module gives different ways of obtaining nonces.
+--
+-- A “nonce” is additional input provided to an encryption algorithm.
+-- The most important rule is that you cannot use the same nonce to encrypt
+-- more than one message. What will happen if you reuse a nonce depends
+-- on the details of the cryptographic algorithm, but, in general, expect
+-- the cryptography to fail completely in this case.
+--
+-- = Random nonce generation
+--
+-- The easiest way to guarantee that nonces do not repeat is to use a
+-- random nonce every time. If the nonce is large enough, then the
+-- probably of a random nonce repeating will be negligible.
+-- All cryptographic functions provided by this library can be
+-- safely used with random nonces.
+--
+-- The 'generate' function is great at generating new random nonces.
+-- The nonces are produced using the standard @random@ package, so
+-- the generation is very fast.
+module Crypto.Sodium.Nonce
+  (
+  -- * Random nonce generation
+    generate
+  ) where
+
+import Data.ByteArray.Sized (SizedByteArray)
+import Data.ByteString (ByteString)
+import GHC.TypeLits (KnownNat)
+
+import Crypto.Sodium.Internal.Random (generateInsecure)
+
+
+-- | Generate a new random nonce.
+--
+-- Note: random data generated by the functions in this module is only suitable
+-- to be used as a nonce, but never as a secret key.
+generate :: KnownNat n => IO (SizedByteArray n ByteString)
+generate = generateInsecure
diff --git a/lib/Crypto/Sodium/Pwhash/Internal.hs b/lib/Crypto/Sodium/Pwhash/Internal.hs
new file mode 100644
--- /dev/null
+++ b/lib/Crypto/Sodium/Pwhash/Internal.hs
@@ -0,0 +1,82 @@
+{-# OPTIONS_GHC -Wno-redundant-constraints #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+-- | Tools for hashing passwords.
+module Crypto.Sodium.Pwhash.Internal
+  ( Algorithm (..)
+  , Params (..)
+  , Salt
+
+  , pwhash
+  ) where
+
+import Prelude hiding (length)
+
+import Data.ByteArray (ByteArrayAccess, length, withByteArray)
+import Data.ByteArray.Sized (ByteArrayN, SizedByteArray, allocRet)
+import Data.Proxy (Proxy (Proxy))
+import Data.Word (Word64)
+import GHC.TypeLits (type (<=), natVal)
+import Foreign.C.Types (CInt, CSize (CSize), CULLong (CULLong))
+
+import qualified Libsodium as Na
+
+
+-- | Secure hashing algorithm.
+data Algorithm
+  = Argon2i_1_3 -- ^ Argon2i version 1.3
+  | Argon2id_1_3 -- ^ Argon2id version 1.3
+  deriving (Eq, Ord, Show)
+
+algorithmToInt :: Algorithm -> CInt
+algorithmToInt Argon2i_1_3 = Na.crypto_pwhash_alg_argon2i13
+algorithmToInt Argon2id_1_3 = Na.crypto_pwhash_alg_argon2id13
+
+
+-- | Secure-hashing parameters.
+data Params = Params
+  { opsLimit :: !Word64  -- ^ Maximum amount of computation to perform.
+  , memLimit :: !Word64  -- ^ Maximum amount of RAM (bytes) to use.
+  }
+  deriving (Eq, Ord, Show)
+
+
+-- | Salt used for password hashing.
+--
+-- This type is parametrised by the actual data type that contains
+-- bytes. This can be, for example, a @ByteString@.
+type Salt a = SizedByteArray Na.CRYPTO_PWHASH_SALTBYTES a
+
+
+-- | Securely hash a password.
+--
+-- This is @crypto_pwhash@, it can be used for key derivation.
+pwhash
+  ::  forall passwd salt n hash.
+      ( ByteArrayAccess passwd, ByteArrayAccess salt
+      , ByteArrayN n hash
+      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX
+      )
+  => Algorithm  -- ^ Hashing algorithm.
+  -> Params  -- ^ Hashing parameters.
+  -> passwd  -- ^ Password to hash.
+  -> Salt salt  -- ^ Hashing salt.
+  -> IO (Maybe hash)
+pwhash alg Params{opsLimit, memLimit} passwd salt = do
+  (ret, hash) <-
+    allocRet (Proxy :: Proxy n) $ \hashPtr ->
+    withByteArray passwd $ \passwdPtr ->
+    withByteArray salt $ \saltPtr -> do
+      Na.crypto_pwhash hashPtr (fromIntegral $ natVal (Proxy :: Proxy n))
+        passwdPtr (fromIntegral $ length passwd)
+        saltPtr
+        (CULLong opsLimit) (CSize memLimit) (algorithmToInt alg)
+  if ret == 0 then
+    pure $ Just hash
+  else
+    pure $ Nothing
diff --git a/lib/Crypto/Sodium/Random.hs b/lib/Crypto/Sodium/Random.hs
new file mode 100644
--- /dev/null
+++ b/lib/Crypto/Sodium/Random.hs
@@ -0,0 +1,30 @@
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+-- | Generate cryptographically-secure random data.
+module Crypto.Sodium.Random
+  ( generate
+  ) where
+
+import Data.ByteArray (ByteArray)
+import Data.ByteArray.Sized (SizedByteArray, alloc)
+import Data.Proxy (Proxy (Proxy))
+import GHC.TypeLits (KnownNat, natVal)
+
+import qualified Libsodium as Na
+
+
+-- | Generate a sequence of cryptographically-secure random bytes.
+--
+-- The output of this function is suitable to generate secret keys.
+--
+-- Note: This function is not thread-safe until Sodium is initialised.
+-- See "Crypto.Sodium.Init" for details.
+generate
+  :: forall ba n. (ByteArray ba, KnownNat n)
+  => IO (SizedByteArray n ba)
+generate = alloc $ \bytesPtr ->
+    Na.randombytes_buf bytesPtr len
+  where
+    len = fromIntegral $ natVal (Proxy :: Proxy n)
diff --git a/lib/Crypto/Sodium/Sign.hs b/lib/Crypto/Sodium/Sign.hs
new file mode 100644
--- /dev/null
+++ b/lib/Crypto/Sodium/Sign.hs
@@ -0,0 +1,87 @@
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+{-# OPTIONS_HADDOCK not-home #-}
+
+-- ! This module merely re-exports definitions from the corresponding
+-- ! module in NaCl and alters the Haddock to make it more specific
+-- ! to crypto-sodium. So, the docs should be kept more-or-less in sync.
+
+-- | Public-key signatures.
+--
+-- It is best to import this module qualified:
+--
+-- @
+-- import qualified Crypto.Sodium.Sign as Sign
+--
+-- signed = Sign.'create' sk message
+-- verified = Sign.'open' pk signed
+-- @
+--
+-- Functions in this modules work with /combined/ signatures.
+-- This means that when you sign a message, it will be copied as is
+-- and then a signature will be prepended. So you should treat the
+-- resulting value as a transparent (because it is not encrypted)
+-- package with a signature attached on top.
+--
+-- Instead of accessing the message directly, you should use
+-- 'open', which will verify the signature and return a copy of the
+-- original message only if the signature was valid.
+module Crypto.Sodium.Sign
+  (
+  -- * Keys
+    PublicKey
+  , toPublicKey
+  , SecretKey
+  , toSecretKey
+  , keypair
+  , keypairFromSeed
+  , unsafeKeypairFromSeed
+
+  -- * Signing/verifying
+  , create
+  , open
+  ) where
+
+import Data.ByteArray (ByteArrayAccess, ScrubbedBytes, withByteArray)
+import Data.ByteString (ByteString)
+import Data.ByteArray.Sized (SizedByteArray, alloc, allocRet)
+import Data.Functor (void)
+import Data.Proxy (Proxy(..))
+import System.IO.Unsafe (unsafePerformIO)
+
+import qualified Libsodium as Na
+
+import NaCl.Sign
+  (PublicKey, SecretKey, create, keypair, open, toPublicKey, toSecretKey)
+
+-- | Seed for deterministically generating a keypair.
+--
+-- In accordance with Libsodium's documentation, the seed must be of size
+-- @Na.CRYPTO_SIGN_SEEDBYTES@.
+--
+-- This type is parametrised by the actual data type that contains
+-- bytes. This can be, for example, a @ByteString@.
+type Seed a = SizedByteArray Na.CRYPTO_SIGN_SEEDBYTES a
+
+
+-- | Generate a new 'SecretKey' together with its 'PublicKey' from a given seed.
+keypairFromSeed
+  :: ByteArrayAccess seed
+  => Seed seed
+  -> IO (PublicKey ByteString, SecretKey ScrubbedBytes)
+keypairFromSeed seed = do
+  allocRet Proxy $ \skPtr ->
+    alloc $ \pkPtr ->
+    withByteArray seed $ \sdPtr ->
+    -- always returns 0, so we don’t check it
+    void $ Na.crypto_sign_seed_keypair pkPtr skPtr sdPtr
+
+-- | Generate a new 'SecretKey' together with its 'PublicKey' from a given seed,
+-- in a pure context.
+unsafeKeypairFromSeed
+  :: ByteArrayAccess seed
+  => Seed seed
+  -> (PublicKey ByteString, SecretKey ScrubbedBytes)
+unsafeKeypairFromSeed = unsafePerformIO . keypairFromSeed
diff --git a/test/Test/Crypto/Encrypt/Public.hs b/test/Test/Crypto/Encrypt/Public.hs
deleted file mode 100644
--- a/test/Test/Crypto/Encrypt/Public.hs
+++ /dev/null
@@ -1,43 +0,0 @@
--- SPDX-FileCopyrightText: 2021 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
--- | “Integration” tests: using Public with our helpers.
-module Test.Crypto.Encrypt.Public where
-
-import Hedgehog (Property, evalMaybe, forAll, property, tripping)
-import Hedgehog.Internal.Property (forAllT)
-
-import Control.Monad.IO.Class (liftIO)
-import Data.ByteArray.Sized (sizedByteArray)
-import Data.ByteString (ByteString)
-
-import qualified Libsodium as Na
-
-import qualified Hedgehog.Gen as G
-import qualified Hedgehog.Range as R
-
-import qualified Crypto.Encrypt.Public as Public
-
-
-nonceSize :: R.Range Int
-nonceSize = R.singleton $ fromIntegral Na.crypto_box_noncebytes
-
-seedSize :: R.Range Int
-seedSize = R.singleton $ fromIntegral Na.crypto_box_seedbytes
-
-
-hprop_encode_decode_seed :: Property
-hprop_encode_decode_seed = property $ do
-    seed1 <- evalMaybe . sizedByteArray =<< forAll (G.bytes seedSize)
-    seed2 <- evalMaybe . sizedByteArray =<< forAll (G.bytes seedSize)
-    (pkS, skS) <- forAllT . liftIO $ Public.keypairFromSeed seed1
-    (pkR, skR) <- forAllT . liftIO $ Public.keypairFromSeed seed2
-    nonceBytes <- forAll $ G.bytes nonceSize
-    let Just nonce = Public.toNonce nonceBytes
-    msg <- forAll $ G.bytes (R.linear 0 1_000)
-    tripping msg (encodeBs pkR skS nonce) (decodeBs skR pkS nonce)
-  where
-    -- We need to specify the type of the cyphertext as it is polymorphic
-    encodeBs pkR skS nonce msg = Public.encrypt pkR skS nonce msg :: ByteString
-    decodeBs skR pkS nonce ct = Public.decrypt skR pkS nonce ct :: Maybe ByteString
diff --git a/test/Test/Crypto/Encrypt/Symmetric.hs b/test/Test/Crypto/Encrypt/Symmetric.hs
deleted file mode 100644
--- a/test/Test/Crypto/Encrypt/Symmetric.hs
+++ /dev/null
@@ -1,32 +0,0 @@
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
--- | “Integration” tests: using Symmetric with our helpers.
-module Test.Crypto.Encrypt.Symmetric where
-
-import Hedgehog (Property, forAll, property, tripping)
-import Hedgehog.Internal.Property (forAllT)
-
-import Control.Monad.IO.Class (liftIO)
-import Data.ByteString (ByteString)
-
-import qualified Hedgehog.Gen as G
-import qualified Hedgehog.Range as R
-
-import qualified Crypto.Key as Key (generate)
-import qualified Crypto.Random (generate)
-
-import qualified Crypto.Encrypt.Symmetric as Symmetric
-
-
-hprop_encode_decode :: Property
-hprop_encode_decode = property $ do
-    key <- forAllT $ liftIO Key.generate
-    nonce <- forAllT $ liftIO $ Crypto.Random.generate @ByteString
-    msg <- forAll $ G.bytes (R.linear 0 1_000)
-    tripping msg (encodeBs key nonce) (decodeBs key nonce)
-  where
-    -- We need to specify the type of the cyphertext as it is polymorphic
-    encodeBs key nonce msg = Symmetric.encrypt key nonce msg :: ByteString
-    decodeBs key nonce ct = Symmetric.decrypt key nonce ct :: Maybe ByteString
diff --git a/test/Test/Crypto/Gen.hs b/test/Test/Crypto/Gen.hs
deleted file mode 100644
--- a/test/Test/Crypto/Gen.hs
+++ /dev/null
@@ -1,36 +0,0 @@
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
--- | Hedgehog generators of test data
-module Test.Crypto.Gen where
-
-import Hedgehog (Gen)
-
-import Data.ByteString (ByteString)
-import Data.ByteArray (ByteArray, ScrubbedBytes, convert)
-import Data.ByteArray.Sized (SizedByteArray, sizedByteArray)
-import Data.Maybe (fromJust)
-import Data.Proxy (Proxy (Proxy))
-import GHC.TypeLits (KnownNat, natVal)
-
-import qualified Hedgehog.Gen as G
-import qualified Hedgehog.Range as R
-
-
--- | Generate a random sized byte array
-sizedBytes
-  :: forall n ba. (ByteArray ba, KnownNat n)
-  => Gen (SizedByteArray n ba)
-sizedBytes = fromJust . sizedByteArray . convert <$>
-  G.bytes (R.singleton $ fromIntegral $ natVal (Proxy @n))
-
--- | Generate a random nonce of the right size.
-nonce :: forall n. KnownNat n => Gen (SizedByteArray n ByteString)
-nonce = sizedBytes
-
--- | Generate a random key of the right size.
---
--- THIS FUNCTION IS NOT SECURE AND IS ONLY SUITABLE FOR TESTS.
-key :: forall n. KnownNat n => Gen (SizedByteArray n ScrubbedBytes)
-key = sizedBytes
diff --git a/test/Test/Crypto/Key/Derivation.hs b/test/Test/Crypto/Key/Derivation.hs
deleted file mode 100644
--- a/test/Test/Crypto/Key/Derivation.hs
+++ /dev/null
@@ -1,49 +0,0 @@
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
--- | Tests for our cool slip-based KDF
-module Test.Crypto.Key.Derivation where
-
-import Hedgehog (Gen, Property, (===), evalIO, forAll, property, tripping)
-
-import qualified Hedgehog.Gen as G
-import qualified Hedgehog.Range as R
-
-import Data.ByteString (ByteString)
-import Data.ByteArray.Sized (SizedByteArray)
-
-import qualified Libsodium as Na
-
-import qualified Test.Crypto.Gen as G
-
-import Crypto.Key (Params (Params), derive, rederive)
-
-import qualified Crypto.Key.Internal as KI
-
-
-genParams :: Gen Params
-genParams = Params
-  <$> G.integral
-    (R.linear (fromIntegral Na.crypto_pwhash_opslimit_min) 10)
-  <*> G.integral
-    (R.linear (fromIntegral Na.crypto_pwhash_memlimit_min) (2 * 1024 * 1024))
-
-genSlipData :: Gen KI.DerivationSlipData
-genSlipData = KI.DerivationSlipData
-  <$> genParams
-  <*> G.nonce
-
-
-hprop_slip_encode_decode :: Property
-hprop_slip_encode_decode = property $ do
-    slipData <- forAll $ genSlipData
-    tripping slipData KI.derivationSlipEncode KI.derivationSlipDecode
-
-hprop_derive_rederive :: Property
-hprop_derive_rederive = property $ do
-    params <- forAll $ genParams
-    passwd <- forAll $ G.bytes (R.linear 0 100)
-    Just (key, slip) <- evalIO $
-      derive @(SizedByteArray 64 ByteString) params passwd
-    rederive slip passwd === Just key
diff --git a/test/Test/Crypto/Nonce.hs b/test/Test/Crypto/Nonce.hs
deleted file mode 100644
--- a/test/Test/Crypto/Nonce.hs
+++ /dev/null
@@ -1,69 +0,0 @@
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
-module Test.Crypto.Nonce where
-
-import Test.HUnit ((@?), (@?=), Assertion)
-
-import Control.DeepSeq (deepseq)
-import Data.ByteArray.Sized (unSizedByteArray)
-import Data.ByteString (ByteString)
-import Data.Ratio ((%))
-import System.CPUTime (getCPUTime)
-
-import qualified Data.ByteString as BS
-import qualified Libsodium as Na
-
-import Crypto.Nonce (generate)
-
-import qualified Crypto.Encrypt.Symmetric as Symmetric
-import qualified Crypto.Nonce as Nonce (generate)
-import qualified Crypto.Pwhash.Internal as Pwhash
-import qualified Crypto.Random as Random (generate)
-
-
--- Well, this is kinda stupid, because we merely generate one random sequence,
--- but this is just to check that the lengths are correctly propagated
--- through types. So, good enough.
-
-unit_generate_Symmetric_nonce :: Assertion
-unit_generate_Symmetric_nonce = do
-  nonce <- generate :: IO (Symmetric.Nonce ByteString)
-  let bs = unSizedByteArray nonce
-  BS.length bs @?= fromIntegral Na.crypto_secretbox_noncebytes
-
-unit_generate_Pwhash_salt :: Assertion
-unit_generate_Pwhash_salt = do
-  nonce <- generate :: IO (Pwhash.Salt ByteString)
-  let bs = unSizedByteArray nonce
-  BS.length bs @?= fromIntegral Na.crypto_pwhash_saltbytes
-
-
-
--- Benchmark to make sure this all makes sense and insecure nonse generation
--- is actually faster than cryptographically-secure generation.
-unit_bench_against_crypto :: Assertion
-unit_bench_against_crypto = do
-    tNonce <- measure $ (unSizedByteArray <$> Nonce.generate @64)
-    tCrypto <- measure $ (unSizedByteArray <$> Random.generate @ByteString @64)
-    let ratio = fromRational (tNonce % tCrypto) :: Double
-
-    -- XXX: The benchmark is disabled, because we don’t yet have an
-    -- implementation that would actually be faster :/.
-    --        vvvvvvvv
-    ratio < 1 || True @? "Crypto gen is " <> show ratio <> "x faster"
-    --        ^^^^^^^^
-  where
-    measure act = do
-        t1 <- getCPUTime
-        go 1000
-        t2 <- getCPUTime
-        pure $ t2 - t1
-      where
-        go :: Int -> IO ()
-        go n
-          | n <= 0 = pure ()
-          | otherwise = do
-              res <- act
-              res `deepseq` go (n - 1)
diff --git a/test/Test/Crypto/Pwhash.hs b/test/Test/Crypto/Pwhash.hs
deleted file mode 100644
--- a/test/Test/Crypto/Pwhash.hs
+++ /dev/null
@@ -1,278 +0,0 @@
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
-{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
-{-# LANGUAGE AllowAmbiguousTypes #-}
-{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE TypeOperators #-}
-
-module Test.Crypto.Pwhash where
-
-import Test.HUnit ((@?=), Assertion)
-
-import Data.ByteArray.Sized (sizedByteArray)
-import Data.ByteString (ByteString)
-import Data.ByteString.Base16 (decodeBase16)
-import Data.Either (fromRight)
-import GHC.TypeLits (type (<=), KnownNat)
-
-import qualified Data.ByteString as BS
-import qualified Libsodium as Na
-
-import Crypto.Pwhash.Internal (Algorithm (..), Params (Params), pwhash)
-
-
-pwhash_test_vector
-  ::  forall n. -- ^ Output length.
-      ( KnownNat n
-      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX
-      )
-  => ByteString  -- ^ Expected hash.
-  -> ByteString  -- ^ Password.
-  -> ByteString  -- ^ Salt.
-  -> Algorithm  -- ^ Hashing algorithm.
-  -> Params  -- ^ Hashing params.
-  -> Assertion
-pwhash_test_vector hash passwd salt alg params = do
-  let hash' = fromRight (error "impossible") . decodeBase16 $ hash
-  let passwd' = fromRight (error "impossible") . decodeBase16 $ passwd
-  let salt' = fromRight (error "impossible") . decodeBase16 $ salt
-  let Just salt'N = sizedByteArray (BS.take 16 salt')  -- Note:
-    -- for some reason, the test vectors in the file are 32 bytes long,
-    -- while the pwhash function needs a 16-byte salt :/
-  let Just hash'N = sizedByteArray @n hash'
-  result <- pwhash alg params passwd' salt'N
-  result @?= Just hash'N
-
-
--- Test vectors from
--- https://github.com/jedisct1/libsodium/blob/f911b56650b680ecfc5d32b11b090849fc2b5f92/test/default/pwhash_argon2id.c
-
-unit_pwhash_argon2id_1 :: Assertion
-unit_pwhash_argon2id_1 =
-  pwhash_test_vector
-    @155
-    "18acec5d6507739f203d1f5d9f1d862f7c2cdac4f19d2bdff64487e60d969e3ced615337b9eec6ac4461c6ca07f0939741e57c24d0005c7ea171a0ee1e7348249d135b38f222e4dad7b9a033ed83f5ca27277393e316582033c74affe2566a2bea47f91f0fd9fe49ece7e1f79f3ad6e9b23e0277c8ecc4b313225748dd2a80f5679534a0700e246a79a49b3f74eb89ec6205fe1eeb941c73b1fcf1"
-    (mconcat $
-      [ "a347ae92bce9f80f6f595a4480fc9c2fe7e7d7148d371e9487d75f5c23008ffae0"
-      , "65577a928febd9b1973a5a95073acdbeb6a030cfc0d79caa2dc5cd011cef02c08d"
-      , "a232d76d52dfbca38ca8dcbd665b17d1665f7cf5fe59772ec909733b24de97d6f5"
-      , "8d220b20c60d7c07ec1fd93c52c31020300c6c1facd77937a597c7a6"
-      ]
-    )
-    "5541fbc995d5c197ba290346d2c559dedf405cf97e5f95482143202f9e74f5c2"
-    Argon2id_1_3
-    (Params 5 7256678)
-
-unit_pwhash_argon2id_2 :: Assertion
-unit_pwhash_argon2id_2 =
-  pwhash_test_vector
-    @250
-    "26bab5f101560e48c711da4f05e81f5a3802b7a93d5155b9cab153069cc42b8e9f910bfead747652a0708d70e4de0bada37218bd203a1201c36b42f9a269b675b1f30cfc36f35a3030e9c7f57dfba0d341a974c1886f708c3e8297efbfe411bb9d51375264bd7c70d57a8a56fc9de2c1c97c08776803ec2cd0140bba8e61dc0f4ad3d3d1a89b4b710af81bfe35a0eea193e18a6da0f5ec05542c9eefc4584458e1da715611ba09617384748bd43b9bf1f3a6df4ecd091d0875e08d6e2fd8a5c7ce08904b5160cd38167b76ec76ef2d310049055a564da23d4ebd2b87e421cc33c401e12d5cd8d936c9baf75ebdfb557d342d2858fc781da31860"
-    (mconcat $
-      [ "e125cee61c8cb7778d9e5ad0a6f5d978ce9f84de213a8556d9ffe202020ab4a6ed"
-      , "9074a4eb3416f9b168f137510f3a30b70b96cbfa219ff99f6c6eaffb15c06b60e0"
-      , "0cc2890277f0fd3c622115772f7048adaebed86e"
-      ]
-    )
-    "f1192dd5dc2368b9cd421338b22433455ee0a3699f9379a08b9650ea2c126f0d"
-    Argon2id_1_3
-    (Params 4 7849083)
-
-unit_pwhash_argon2id_3 :: Assertion
-unit_pwhash_argon2id_3 =
-  pwhash_test_vector
-    @249
-    "6eb45e668582d63788ca8f6e930ca60b045a795fca987344f9a7a135aa3b5132b50a34a3864c26581f1f56dd0bcbfafbfa92cd9bff6b24a734cfe88f854aef4bda0a7983120f44936e8ff31d29728ac08ccce6f3f916b3c63962755c23a1fa9bb4e8823fc867bfd18f28980d94bc5874423ab7f96cc0ab78d8fa21fbd00cd3a1d96a73fa439ccc3fc4eab1590677b06cc78b0f674dfb680f23022fb902022dd8620803229c6ddf79a8156ccfce48bbd76c05ab670634f206e5b2e896230baa74a856964dbd8511acb71d75a1506766a125d8ce037f1db72086ebc3bccaefbd8cd9380167c2530386544ebfbeadbe237784d102bb92a10fd242"
-    (mconcat $
-      [ "92263cbf6ac376499f68a4289d3bb59e5a22335eba63a32e6410249155b956b6a3"
-      , "b48d4a44906b18b897127300b375b8f834f1ceffc70880a885f47c33876717e392"
-      , "be57f7da3ae58da4fd1f43daa7e44bb82d3717af4319349c24cd31e46d295856b0"
-      , "441b6b289992a11ced1cc3bf3011604590244a3eb737ff221129215e4e4347f491"
-      , "5d41292b5173d196eb9add693be5319fdadc242906178bb6c0286c9b6ca6012746"
-      , "711f58c8c392016b2fdfc09c64f0f6b6ab7b"
-      ]
-    )
-    "3b840e20e9555e9fb031c4ba1f1747ce25cc1d0ff664be676b9b4a90641ff194"
-    Argon2id_1_3
-    (Params 3 7994791)
-
--- unit_pwhash_argon2id_4 is skipped because it is a test for an incorrect output
--- size, which is impossible due to stronger types in this library
-
-unit_pwhash_argon2id_5 :: Assertion
-unit_pwhash_argon2id_5 =
-  pwhash_test_vector
-    @190
-    "08d8cd330c57e1b4643241d05bb468ba4ee4e932cd0858816be9ef15360b27bbd06a87130ee92222be267a29b81f5ae8fe8613324cfc4832dc49387fd0602f1c57b4d0f3855db94fb7e12eb05f9a484aed4a4307abf586cd3d55c809bc081541e00b682772fb2066504ff935b8ebc551a2083882f874bc0fae68e56848ae34c91097c3bf0cca8e75c0797eef3efde3f75e005815018db3cf7c109a812264c4de69dcb22322dbbcfa447f5b00ecd1b04a7be1569c8e556adb7bba48adf81d"
-    (mconcat $
-      [ "4a857e2ee8aa9b6056f2424e84d24a72473378906ee04a46cb05311502d5250b82"
-      , "ad86b83c8f20a23dbb74f6da60b0b6ecffd67134d45946ac8ebfb3064294bc097d"
-      , "43ced68642bfb8bbbdd0f50b30118f5e"
-      ]
-    )
-    "39d82eef32010b8b79cc5ba88ed539fbaba741100f2edbeca7cc171ffeabf258"
-    Argon2id_1_3
-    (Params 3 1432947)
-
-unit_pwhash_argon2id_6 :: Assertion
-unit_pwhash_argon2id_6 =
-  pwhash_test_vector
-    @178
-    "d6e9d6cabd42fb9ba7162fe9b8e41d59d3c7034756cb460c9affe393308bd0225ce0371f2e6c3ca32aca2002bf2d3909c6b6e7dfc4a00e850ff4f570f8f749d4bb6f0091e554be67a9095ae1eefaa1a933316cbec3c2fd4a14a5b6941bda9b7eabd821d79abde2475a53af1a8571c7ee46460be415882e0b393f48c12f740a6a72cba9773000602e13b40d3dfa6ac1d4ec43a838b7e3e165fecad4b2498389e60a3ff9f0f8f4b9fca1126e64f49501e38690"
-    (mconcat $
-      [ "c7b09aec680e7b42fedd7fc792e78b2f6c1bea8f4a884320b648f81e8cf515e8ba"
-      , "9dcfb11d43c4aae114c1734aa69ca82d44998365db9c93744fa28b63fd16000e82"
-      , "61cbbe083e7e2da1e5f696bde0834fe53146d7e0e35e7de9920d041f5a5621aabe"
-      , "02da3e2b09b405b77937efef3197bd5772e41fdb73fb5294478e45208063b5f58e"
-      , "089dbeb6d6342a909c1307b3fff5fe2cf4da56bdae50848f"
-      ]
-    )
-    "039c056d933b475032777edbaffac50f143f64c123329ed9cf59e3b65d3f43b6"
-    Argon2id_1_3
-    (Params 3 4886999)
-
-unit_pwhash_argon2id_7 :: Assertion
-unit_pwhash_argon2id_7 =
-  pwhash_test_vector
-    @231
-    "7fb72409b0987f8190c3729710e98c3f80c5a8727d425fdcde7f3644d467fe973f5b5fee683bd3fce812cb9ae5e9921a2d06c2f1905e4e839692f2b934b682f11a2fe2b90482ea5dd234863516dba6f52dc0702d324ec77d860c2e181f84472bd7104fedce071ffa93c5309494ad51623d214447a7b2b1462dc7d5d55a1f6fd5b54ce024118d86f0c6489d16545aaa87b6689dad9f2fb47fda9894f8e12b87d978b483ccd4cc5fd9595cdc7a818452f915ce2f7df95ec12b1c72e3788d473441d884f9748eb14703c21b45d82fd667b85f5b2d98c13303b3fe76285531a826b6fc0fe8e3dddecf"
-    (mconcat $
-      [ "b540beb016a5366524d4605156493f9874514a5aa58818cd0c6dfffaa9e90205f1"
-      , "7b"
-      ]
-    )
-    "44071f6d181561670bda728d43fb79b443bb805afdebaf98622b5165e01b15fb"
-    Argon2id_1_3
-    (Params 1 1631659)
-
-unit_pwhash_argon2id_8 :: Assertion
-unit_pwhash_argon2id_8 =
-  pwhash_test_vector
-    @167
-    "4e702bc5f891df884c6ddaa243aa846ce3c087fe930fef0f36b3c2be34164ccc295db509254743f18f947159c813bcd5dd8d94a3aec93bbe57605d1fad1aef1112687c3d4ef1cb329d21f1632f626818d766915d886e8d819e4b0b9c9307f4b6afc081e13b0cf31db382ff1bf05a16aac7af696336d75e99f82163e0f371e1d25c4add808e215697ad3f779a51a462f8bf52610af21fc69dba6b072606f2dabca7d4ae1d91d919"
-    (mconcat $
-      [ "a14975c26c088755a8b715ff2528d647cd343987fcf4aa25e7194a8417fb2b4b3f"
-      , "7268da9f3182b4cfb22d138b2749d673a47ecc7525dd15a0a3c66046971784bb63"
-      , "d7eae24cc84f2631712075a10e10a96b0e0ee67c43e01c423cb9c44e5371017e9c"
-      , "496956b632158da3fe12addecb88912e6759bc37f9af2f45af72c5cae3b179ffb6"
-      , "76a697de6ebe45cd4c16d4a9d642d29ddc0186a0a48cb6cd62bfc3dd229d313b30"
-      , "1560971e740e2cf1f99a9a090a5b283f35475057e96d7064e2e0fc81984591068d"
-      , "55a3b4169f22cccb0745a2689407ea1901a0a766eb99"
-      ]
-    )
-    "3d968b2752b8838431165059319f3ff8910b7b8ecb54ea01d3f54769e9d98daf"
-    Argon2id_1_3
-    (Params 3 1784128)
-
-
--- Test vectors from
--- https://github.com/jedisct1/libsodium/blob/f911b56650b680ecfc5d32b11b090849fc2b5f92/test/default/pwhash_argon2i.c
-
-unit_pwhash_argon2i_1 :: Assertion
-unit_pwhash_argon2i_1 =
-  pwhash_test_vector
-    @155
-    "23b803c84eaa25f4b44634cc1e5e37792c53fcd9b1eb20f865329c68e09cbfa9f1968757901b383fce221afe27713f97914a041395bbe1fb70e079e5bed2c7145b1f6154046f5958e9b1b29055454e264d1f2231c316f26be2e3738e83a80315e9a0951ce4b137b52e7d5ee7b37f7d936dcee51362bcf792595e3c896ad5042734fc90c92cae572ce63ff659a2f7974a3bd730d04d525d253ccc38"
-    (mconcat $
-      [ "a347ae92bce9f80f6f595a4480fc9c2fe7e7d7148d371e9487d75f5c23008ffae0"
-      , "65577a928febd9b1973a5a95073acdbeb6a030cfc0d79caa2dc5cd011cef02c08d"
-      , "a232d76d52dfbca38ca8dcbd665b17d1665f7cf5fe59772ec909733b24de97d6f5"
-      , "8d220b20c60d7c07ec1fd93c52c31020300c6c1facd77937a597c7a6"
-      ]
-    )
-    "5541fbc995d5c197ba290346d2c559dedf405cf97e5f95482143202f9e74f5c2"
-    Argon2i_1_3
-    (Params 5 7256678)
-
-unit_pwhash_argon2i_2 :: Assertion
-unit_pwhash_argon2i_2 =
-  pwhash_test_vector
-    @250
-    "0bb3769b064b9c43a9460476ab38c4a9a2470d55d4c992c6e723af895e4c07c09af41f22f90eab583a0c362d177f4677f212482fd145bfb9ac6211635e48461122bb49097b5fb0739d2cd22a39bf03d268e7495d4fd8d710aa156202f0a06e932ff513e6e7c76a4e98b6df5cf922f124791b1076ad904e6897271f5d7d24c5929e2a3b836d0f2f2697c2d758ee79bf1264f3fae65f3744e0f6d7d07ef6e8b35b70c0f88e9036325bfb24ac7f550351486da87aef10d6b0cb77d1cf6e31cf98399c6f241c605c6530dffb4764784f6c0b0bf601d4e4431e8b18dabdc3079c6e264302ade79f61cbd5497c95486340bb891a737223100be0429650"
-    (mconcat $
-      [ "e125cee61c8cb7778d9e5ad0a6f5d978ce9f84de213a8556d9ffe202020ab4a6ed"
-      , "9074a4eb3416f9b168f137510f3a30b70b96cbfa219ff99f6c6eaffb15c06b60e0"
-      , "0cc2890277f0fd3c622115772f7048adaebed86e"
-      ]
-    )
-    "f1192dd5dc2368b9cd421338b22433455ee0a3699f9379a08b9650ea2c126f0d"
-    Argon2i_1_3
-    (Params 4 7849083)
-
-unit_pwhash_argon2i_3 :: Assertion
-unit_pwhash_argon2i_3 =
-  pwhash_test_vector
-    @249
-    "e9aa073b0b872f15c083d1d7ce52c09f493b827ca78f13a06c1721b45b1e17b24c04e19fe869333135360197a7eb55994fee3e8d9680aedfdf7674f3ad7b84d59d7eab03579ffc10c7093093bc48ec84252aa1b30f40f5e838f1443e15e2772a39f4e774eb052097e8881e94f15457b779fa2af2bbc9a993687657c7704ac8a37c25c1df4289eb4c70da45f2fd46bc0f78259767d3dd478a7c369cf866758bc36d9bd8e2e3c9fb0cf7fd6073ebf630c1f67fa7d303c07da40b36749d157ea37965fef810f2ea05ae6fc7d96a8f3470d73e15b22b42e8d6986dbfe5303256b2b3560372c4452ffb2a04fb7c6691489f70cb46831be0679117f7"
-    (mconcat $
-      [ "92263cbf6ac376499f68a4289d3bb59e5a22335eba63a32e6410249155b956b6a3"
-      , "b48d4a44906b18b897127300b375b8f834f1ceffc70880a885f47c33876717e392"
-      , "be57f7da3ae58da4fd1f43daa7e44bb82d3717af4319349c24cd31e46d295856b0"
-      , "441b6b289992a11ced1cc3bf3011604590244a3eb737ff221129215e4e4347f491"
-      , "5d41292b5173d196eb9add693be5319fdadc242906178bb6c0286c9b6ca6012746"
-      , "711f58c8c392016b2fdfc09c64f0f6b6ab7b"
-      ]
-    )
-    "3b840e20e9555e9fb031c4ba1f1747ce25cc1d0ff664be676b9b4a90641ff194"
-    Argon2i_1_3
-    (Params 3 7994791)
-
--- unit_pwhash_argon2i_4 is skipped because it is a test for an incorrect output
--- size, which is impossible due to stronger types in this library
-
-unit_pwhash_argon2i_5 :: Assertion
-unit_pwhash_argon2i_5 =
-  pwhash_test_vector
-    @190
-    "c121209f0ba70aed93d49200e5dc82cce013cef25ea31e160bf8db3cf448a59d1a56f6c19259e18ea020553cb75781761d112b2d949a297584c65e60df95ad89c4109825a3171dc6f20b1fd6b0cdfd194861bc2b414295bee5c6c52619e544abce7d520659c3d51de2c60e89948d830695ab38dcb75dd7ab06a4770dd4bc7c8f335519e04b038416b1a7dbd25c026786a8105c5ffe7a0931364f0376ae5772be39b51d91d3281464e0f3a128e7155a68e87cf79626ffca0b2a3022fc8420"
-    (mconcat $
-      [ "4a857e2ee8aa9b6056f2424e84d24a72473378906ee04a46cb05311502d5250b82"
-      , "ad86b83c8f20a23dbb74f6da60b0b6ecffd67134d45946ac8ebfb3064294bc097d"
-      , "43ced68642bfb8bbbdd0f50b30118f5e"
-      ]
-    )
-    "39d82eef32010b8b79cc5ba88ed539fbaba741100f2edbeca7cc171ffeabf258"
-    Argon2i_1_3
-    (Params 3 1432947)
-
-unit_pwhash_argon2i_6 :: Assertion
-unit_pwhash_argon2i_6 =
-  pwhash_test_vector
-    @178
-    "91c337ce8918a5805a59b00bd1819d3eb4356807cbd2a80b271c4b482dce03f5b02ae4eb831ff668cbb327b93c300b41da4852e5547bea8342d518dd9311aaeb5f90eccf66d548f9275631f0b1fd4b299cec5d2e86a59e55dc7b3afab6204447b21d1ef1da824abaf31a25a0d6135c4fe81d34a06816c8a6eab19141f5687108500f3719a862af8c5fee36e130c69921e11ce83dfc72c5ec3b862c1bccc5fd63ad57f432fbcca6f9e18d5a59015950cdf053"
-    (mconcat $
-      [ "c7b09aec680e7b42fedd7fc792e78b2f6c1bea8f4a884320b648f81e8cf515e8ba"
-      , "9dcfb11d43c4aae114c1734aa69ca82d44998365db9c93744fa28b63fd16000e82"
-      , "61cbbe083e7e2da1e5f696bde0834fe53146d7e0e35e7de9920d041f5a5621aabe"
-      , "02da3e2b09b405b77937efef3197bd5772e41fdb73fb5294478e45208063b5f58e"
-      , "089dbeb6d6342a909c1307b3fff5fe2cf4da56bdae50848f"
-      ]
-    )
-    "039c056d933b475032777edbaffac50f143f64c123329ed9cf59e3b65d3f43b6"
-    Argon2i_1_3
-    (Params 3 4886999)
-
--- unit_pwhash_argon2i_7 is skipped because it is a test for an incorrect
--- opslimit.
--- XXX: Maybe we could encode this restriction in types?
-
-unit_pwhash_argon2i_8 :: Assertion
-unit_pwhash_argon2i_8 =
-  pwhash_test_vector
-    @167
-    "e942951dfbc2d508294b10f9e97b47d0cd04e668a043cb95679cc1139df7c27cd54367688725be9d069f5704c12223e7e4ca181fbd0bed18bb4634795e545a6c04a7306933a41a794baedbb628d41bc285e0b9084055ae136f6b63624c874f5a1e1d8be7b0b7227a171d2d7ed578d88bfdcf18323198962d0dcad4126fd3f21adeb1e11d66252ea0c58c91696e91031bfdcc2a9dc0e028d17b9705ba2d7bcdcd1e3ba75b4b1fea"
-    (mconcat $
-      [ "a14975c26c088755a8b715ff2528d647cd343987fcf4aa25e7194a8417fb2b4b3f"
-      , "7268da9f3182b4cfb22d138b2749d673a47ecc7525dd15a0a3c66046971784bb63"
-      , "d7eae24cc84f2631712075a10e10a96b0e0ee67c43e01c423cb9c44e5371017e9c"
-      , "496956b632158da3fe12addecb88912e6759bc37f9af2f45af72c5cae3b179ffb6"
-      , "76a697de6ebe45cd4c16d4a9d642d29ddc0186a0a48cb6cd62bfc3dd229d313b30"
-      , "1560971e740e2cf1f99a9a090a5b283f35475057e96d7064e2e0fc81984591068d"
-      , "55a3b4169f22cccb0745a2689407ea1901a0a766eb99"
-      ]
-    )
-    "3d968b2752b8838431165059319f3ff8910b7b8ecb54ea01d3f54769e9d98daf"
-    Argon2i_1_3
-    (Params 3 1784128)
diff --git a/test/Test/Crypto/Random.hs b/test/Test/Crypto/Random.hs
deleted file mode 100644
--- a/test/Test/Crypto/Random.hs
+++ /dev/null
@@ -1,36 +0,0 @@
--- SPDX-FileCopyrightText: 2020 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
-module Test.Crypto.Random where
-
-import Test.HUnit ((@?=), Assertion)
-
-import Data.ByteArray.Sized (unSizedByteArray)
-import Data.ByteString (ByteString)
-
-import qualified Data.ByteString as BS
-import qualified Libsodium as Na
-
-import Crypto.Random (generate)
-
-import qualified Crypto.Encrypt.Symmetric as Symmetric
-
-
--- Well, this is kinda stupid, because we merely generate one random sequence,
--- but this is just to check that the lengths are correctly propagated
--- through types. So, good enough.
---
--- Also it is not thread-safe, since we don’t call @sodiumInit@...
-
-unit_generate_Symmetric_key :: Assertion
-unit_generate_Symmetric_key = do
-  key <- generate :: IO (Symmetric.Key ByteString)
-  let bs = unSizedByteArray key
-  BS.length bs @?= fromIntegral Na.crypto_secretbox_keybytes
-
-unit_generate_Symmetric_nonce :: Assertion
-unit_generate_Symmetric_nonce = do
-  nonce <- generate :: IO (Symmetric.Nonce ByteString)
-  let bs = unSizedByteArray nonce
-  BS.length bs @?= fromIntegral Na.crypto_secretbox_noncebytes
diff --git a/test/Test/Crypto/Sign.hs b/test/Test/Crypto/Sign.hs
deleted file mode 100644
--- a/test/Test/Crypto/Sign.hs
+++ /dev/null
@@ -1,36 +0,0 @@
--- SPDX-FileCopyrightText: 2021 Serokell
---
--- SPDX-License-Identifier: MPL-2.0
-
--- | “Integration” tests: using Sign with our helpers.
-module Test.Crypto.Sign where
-
-import Hedgehog (Property, evalMaybe, forAll, property, tripping)
-import Hedgehog.Internal.Property (forAllT)
-
-import Control.Monad.IO.Class (liftIO)
-import Data.ByteArray.Sized (sizedByteArray)
-import Data.ByteString (ByteString)
-
-import qualified Hedgehog.Gen as G
-import qualified Hedgehog.Range as R
-
-import qualified Libsodium as Na
-
-import qualified Crypto.Sign as Sign
-
-
-seedSize :: R.Range Int
-seedSize = R.singleton $ fromIntegral Na.crypto_sign_seedbytes
-
-
-hprop_encode_decode_seed :: Property
-hprop_encode_decode_seed = property $ do
-    seed <- evalMaybe . sizedByteArray =<< forAll (G.bytes seedSize)
-    (pk, sk) <- forAllT . liftIO $ Sign.keypairFromSeed seed
-    msg <- forAll $ G.bytes (R.linear 0 1_000)
-    tripping msg (encodeBs sk) (decodeBs pk)
-  where
-    -- We need to specify the type of the signed msg as it is polymorphic
-    encodeBs sk msg = Sign.create sk msg :: ByteString
-    decodeBs pk ct = Sign.open pk ct :: Maybe ByteString
diff --git a/test/Test/Crypto/Sodium/Encrypt/Public.hs b/test/Test/Crypto/Sodium/Encrypt/Public.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Crypto/Sodium/Encrypt/Public.hs
@@ -0,0 +1,45 @@
+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
+
+-- SPDX-FileCopyrightText: 2021 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+-- | “Integration” tests: using Public with our helpers.
+module Test.Crypto.Sodium.Encrypt.Public where
+
+import Hedgehog (Property, evalMaybe, forAll, property, tripping)
+import Hedgehog.Internal.Property (forAllT)
+
+import Control.Monad.IO.Class (liftIO)
+import Data.ByteArray.Sized (sizedByteArray)
+import Data.ByteString (ByteString)
+
+import qualified Libsodium as Na
+
+import qualified Hedgehog.Gen as G
+import qualified Hedgehog.Range as R
+
+import qualified Crypto.Sodium.Encrypt.Public as Public
+
+
+nonceSize :: R.Range Int
+nonceSize = R.singleton $ fromIntegral Na.crypto_box_noncebytes
+
+seedSize :: R.Range Int
+seedSize = R.singleton $ fromIntegral Na.crypto_box_seedbytes
+
+
+hprop_encode_decode_seed :: Property
+hprop_encode_decode_seed = property $ do
+    seed1 <- evalMaybe . sizedByteArray =<< forAll (G.bytes seedSize)
+    seed2 <- evalMaybe . sizedByteArray =<< forAll (G.bytes seedSize)
+    (pkS, skS) <- forAllT . liftIO $ Public.keypairFromSeed seed1
+    (pkR, skR) <- forAllT . liftIO $ Public.keypairFromSeed seed2
+    nonceBytes <- forAll $ G.bytes nonceSize
+    let Just nonce = Public.toNonce nonceBytes
+    msg <- forAll $ G.bytes (R.linear 0 1_000)
+    tripping msg (encodeBs pkR skS nonce) (decodeBs skR pkS nonce)
+  where
+    -- We need to specify the type of the cyphertext as it is polymorphic
+    encodeBs pkR skS nonce msg = Public.encrypt pkR skS nonce msg :: ByteString
+    decodeBs skR pkS nonce ct = Public.decrypt skR pkS nonce ct :: Maybe ByteString
diff --git a/test/Test/Crypto/Sodium/Encrypt/Symmetric.hs b/test/Test/Crypto/Sodium/Encrypt/Symmetric.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Crypto/Sodium/Encrypt/Symmetric.hs
@@ -0,0 +1,32 @@
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+-- | “Integration” tests: using Symmetric with our helpers.
+module Test.Crypto.Sodium.Encrypt.Symmetric where
+
+import Hedgehog (Property, forAll, property, tripping)
+import Hedgehog.Internal.Property (forAllT)
+
+import Control.Monad.IO.Class (liftIO)
+import Data.ByteString (ByteString)
+
+import qualified Hedgehog.Gen as G
+import qualified Hedgehog.Range as R
+
+import qualified Crypto.Sodium.Key as Key (generate)
+import qualified Crypto.Sodium.Random (generate)
+
+import qualified Crypto.Sodium.Encrypt.Symmetric as Symmetric
+
+
+hprop_encode_decode :: Property
+hprop_encode_decode = property $ do
+    key <- forAllT $ liftIO Key.generate
+    nonce <- forAllT $ liftIO $ Crypto.Sodium.Random.generate @ByteString
+    msg <- forAll $ G.bytes (R.linear 0 1_000)
+    tripping msg (encodeBs key nonce) (decodeBs key nonce)
+  where
+    -- We need to specify the type of the cyphertext as it is polymorphic
+    encodeBs key nonce msg = Symmetric.encrypt key nonce msg :: ByteString
+    decodeBs key nonce ct = Symmetric.decrypt key nonce ct :: Maybe ByteString
diff --git a/test/Test/Crypto/Sodium/Gen.hs b/test/Test/Crypto/Sodium/Gen.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Crypto/Sodium/Gen.hs
@@ -0,0 +1,36 @@
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+-- | Hedgehog generators of test data
+module Test.Crypto.Sodium.Gen where
+
+import Hedgehog (Gen)
+
+import Data.ByteString (ByteString)
+import Data.ByteArray (ByteArray, ScrubbedBytes, convert)
+import Data.ByteArray.Sized (SizedByteArray, sizedByteArray)
+import Data.Maybe (fromJust)
+import Data.Proxy (Proxy (Proxy))
+import GHC.TypeLits (KnownNat, natVal)
+
+import qualified Hedgehog.Gen as G
+import qualified Hedgehog.Range as R
+
+
+-- | Generate a random sized byte array
+sizedBytes
+  :: forall n ba. (ByteArray ba, KnownNat n)
+  => Gen (SizedByteArray n ba)
+sizedBytes = fromJust . sizedByteArray . convert <$>
+  G.bytes (R.singleton $ fromIntegral $ natVal (Proxy @n))
+
+-- | Generate a random nonce of the right size.
+nonce :: forall n. KnownNat n => Gen (SizedByteArray n ByteString)
+nonce = sizedBytes
+
+-- | Generate a random key of the right size.
+--
+-- THIS FUNCTION IS NOT SECURE AND IS ONLY SUITABLE FOR TESTS.
+key :: forall n. KnownNat n => Gen (SizedByteArray n ScrubbedBytes)
+key = sizedBytes
diff --git a/test/Test/Crypto/Sodium/Hash.hs b/test/Test/Crypto/Sodium/Hash.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Crypto/Sodium/Hash.hs
@@ -0,0 +1,1342 @@
+-- SPDX-FileCopyrightText: 2021 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+
+module Test.Crypto.Sodium.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 GHC.TypeLits (type (<=), KnownNat)
+import Control.Monad (forM_)
+
+import qualified Libsodium as Na
+
+import qualified Crypto.Sodium.Hash as Hash
+
+
+unit_blake2b256_unkeyed :: Assertion
+unit_blake2b256_unkeyed = do
+    let
+      msg = "testing\n" :: ByteString
+      Just hash = sizedByteArray . fromRight (error "impossible") . decodeBase16 $
+          "9ec2c90ec850ccd1b924806046eace8dd3730e631ad8eb73c28b78abba936232"
+    Hash.blake2b @32 msg @?= hash
+
+
+blake2b_test_vector
+  ::  forall len. -- ^ Output length.
+      ( KnownNat len
+      , Na.CRYPTO_GENERICHASH_BYTES_MIN <= len
+      , len <= Na.CRYPTO_GENERICHASH_BYTES_MAX
+      )
+  => ByteString  -- ^ Message
+  -> ByteString  -- ^ Key
+  -> ByteString  -- ^ Expected hash.
+  -> Assertion
+blake2b_test_vector msg key hash = do
+  let hash' = fromRight (error "impossible") . decodeBase16 $ hash
+      key' = fromRight (error "impossible") . decodeBase16 $ key
+      msg' = fromRight (error "impossible") . decodeBase16 $ msg
+      Just hash'N = sizedByteArray @len hash'
+      result = Hash.blake2bWithKey key' msg'
+  result @?= hash'N
+
+unit_blake2b512_keyed_vectors :: Assertion
+unit_blake2b512_keyed_vectors = forM_ vectors $ \(in', key, out) ->
+  blake2b_test_vector @64 in' key out
+
+-- Test vectors from
+-- https://github.com/jedisct1/libsodium/blob/f911b56650b680ecfc5d32b11b090849fc2b5f92/test/default/generichash.c
+
+vectors :: [(ByteString, ByteString, ByteString)]
+vectors = [
+    (
+        "",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568"
+    ),
+    (
+        "00",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd"
+    ),
+    (
+        "0001",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965"
+    ),
+    (
+        "000102",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "33d0825dddf7ada99b0e7e307104ad07ca9cfd9692214f1561356315e784f3e5a17e364ae9dbb14cb2036df932b77f4b292761365fb328de7afdc6d8998f5fc1"
+    ),
+    (
+        "00010203",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "beaa5a3d08f3807143cf621d95cd690514d0b49efff9c91d24b59241ec0eefa5f60196d407048bba8d2146828ebcb0488d8842fd56bb4f6df8e19c4b4daab8ac"
+    ),
+    (
+        "0001020304",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "098084b51fd13deae5f4320de94a688ee07baea2800486689a8636117b46c1f4c1f6af7f74ae7c857600456a58a3af251dc4723a64cc7c0a5ab6d9cac91c20bb"
+    ),
+    (
+        "000102030405",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "6044540d560853eb1c57df0077dd381094781cdb9073e5b1b3d3f6c7829e12066bbaca96d989a690de72ca3133a83652ba284a6d62942b271ffa2620c9e75b1f"
+    ),
+    (
+        "00010203040506",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "7a8cfe9b90f75f7ecb3acc053aaed6193112b6f6a4aeeb3f65d3de541942deb9e2228152a3c4bbbe72fc3b12629528cfbb09fe630f0474339f54abf453e2ed52"
+    ),
+    (
+        "0001020304050607",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "380beaf6ea7cc9365e270ef0e6f3a64fb902acae51dd5512f84259ad2c91f4bc4108db73192a5bbfb0cbcf71e46c3e21aee1c5e860dc96e8eb0b7b8426e6abe9"
+    ),
+    (
+        "000102030405060708",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "60fe3c4535e1b59d9a61ea8500bfac41a69dffb1ceadd9aca323e9a625b64da5763bad7226da02b9c8c4f1a5de140ac5a6c1124e4f718ce0b28ea47393aa6637"
+    ),
+    (
+        "00010203040506070809",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "4fe181f54ad63a2983feaaf77d1e7235c2beb17fa328b6d9505bda327df19fc37f02c4b6f0368ce23147313a8e5738b5fa2a95b29de1c7f8264eb77b69f585cd"
+    ),
+    (
+        "000102030405060708090a",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "f228773ce3f3a42b5f144d63237a72d99693adb8837d0e112a8a0f8ffff2c362857ac49c11ec740d1500749dac9b1f4548108bf3155794dcc9e4082849e2b85b"
+    ),
+    (
+        "000102030405060708090a0b",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "962452a8455cc56c8511317e3b1f3b2c37df75f588e94325fdd77070359cf63a9ae6e930936fdf8e1e08ffca440cfb72c28f06d89a2151d1c46cd5b268ef8563"
+    ),
+    (
+        "000102030405060708090a0b0c",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "43d44bfa18768c59896bf7ed1765cb2d14af8c260266039099b25a603e4ddc5039d6ef3a91847d1088d401c0c7e847781a8a590d33a3c6cb4df0fab1c2f22355"
+    ),
+    (
+        "000102030405060708090a0b0c0d",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "dcffa9d58c2a4ca2cdbb0c7aa4c4c1d45165190089f4e983bb1c2cab4aaeff1fa2b5ee516fecd780540240bf37e56c8bcca7fab980e1e61c9400d8a9a5b14ac6"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "6fbf31b45ab0c0b8dad1c0f5f4061379912dde5aa922099a030b725c73346c524291adef89d2f6fd8dfcda6d07dad811a9314536c2915ed45da34947e83de34e"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "a0c65bddde8adef57282b04b11e7bc8aab105b99231b750c021f4a735cb1bcfab87553bba3abb0c3e64a0b6955285185a0bd35fb8cfde557329bebb1f629ee93"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f10",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "f99d815550558e81eca2f96718aed10d86f3f1cfb675cce06b0eff02f617c5a42c5aa760270f2679da2677c5aeb94f1142277f21c7f79f3c4f0cce4ed8ee62b1"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f1011",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "95391da8fc7b917a2044b3d6f5374e1ca072b41454d572c7356c05fd4bc1e0f40b8bb8b4a9f6bce9be2c4623c399b0dca0dab05cb7281b71a21b0ebcd9e55670"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "04b9cd3d20d221c09ac86913d3dc63041989a9a1e694f1e639a3ba7e451840f750c2fc191d56ad61f2e7936bc0ac8e094b60caeed878c18799045402d61ceaf9"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f10111213",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "ec0e0ef707e4ed6c0c66f9e089e4954b058030d2dd86398fe84059631f9ee591d9d77375355149178c0cf8f8e7c49ed2a5e4f95488a2247067c208510fadc44c"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f1011121314",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "9a37cce273b79c09913677510eaf7688e89b3314d3532fd2764c39de022a2945b5710d13517af8ddc0316624e73bec1ce67df15228302036f330ab0cb4d218dd"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "4cf9bb8fb3d4de8b38b2f262d3c40f46dfe747e8fc0a414c193d9fcf753106ce47a18f172f12e8a2f1c26726545358e5ee28c9e2213a8787aafbc516d2343152"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f10111213141516",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "64e0c63af9c808fd893137129867fd91939d53f2af04be4fa268006100069b2d69daa5c5d8ed7fddcb2a70eeecdf2b105dd46a1e3b7311728f639ab489326bc9"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f1011121314151617",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "5e9c93158d659b2def06b0c3c7565045542662d6eee8a96a89b78ade09fe8b3dcc096d4fe48815d88d8f82620156602af541955e1f6ca30dce14e254c326b88f"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "7775dff889458dd11aef417276853e21335eb88e4dec9cfb4e9edb49820088551a2ca60339f12066101169f0dfe84b098fddb148d9da6b3d613df263889ad64b"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f10111213141516171819",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "f0d2805afbb91f743951351a6d024f9353a23c7ce1fc2b051b3a8b968c233f46f50f806ecb1568ffaa0b60661e334b21dde04f8fa155ac740eeb42e20b60d764"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "86a2af316e7d7754201b942e275364ac12ea8962ab5bd8d7fb276dc5fbffc8f9a28cae4e4867df6780d9b72524160927c855da5b6078e0b554aa91e31cb9ca1d"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "10bdf0caa0802705e706369baf8a3f79d72c0a03a80675a7bbb00be3a45e516424d1ee88efb56f6d5777545ae6e27765c3a8f5e493fc308915638933a1dfee55"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "b01781092b1748459e2e4ec178696627bf4ebafebba774ecf018b79a68aeb84917bf0b84bb79d17b743151144cd66b7b33a4b9e52c76c4e112050ff5385b7f0b"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "c6dbc61dec6eaeac81e3d5f755203c8e220551534a0b2fd105a91889945a638550204f44093dd998c076205dffad703a0e5cd3c7f438a7e634cd59fededb539e"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "eba51acffb4cea31db4b8d87e9bf7dd48fe97b0253ae67aa580f9ac4a9d941f2bea518ee286818cc9f633f2a3b9fb68e594b48cdd6d515bf1d52ba6c85a203a7"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "86221f3ada52037b72224f105d7999231c5e5534d03da9d9c0a12acb68460cd375daf8e24386286f9668f72326dbf99ba094392437d398e95bb8161d717f8991"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "5595e05c13a7ec4dc8f41fb70cb50a71bce17c024ff6de7af618d0cc4e9c32d9570d6d3ea45b86525491030c0d8f2b1836d5778c1ce735c17707df364d054347"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "ce0f4f6aca89590a37fe034dd74dd5fa65eb1cbd0a41508aaddc09351a3cea6d18cb2189c54b700c009f4cbf0521c7ea01be61c5ae09cb54f27bc1b44d658c82"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "7ee80b06a215a3bca970c77cda8761822bc103d44fa4b33f4d07dcb997e36d55298bceae12241b3fa07fa63be5576068da387b8d5859aeab701369848b176d42"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "940a84b6a84d109aab208c024c6ce9647676ba0aaa11f86dbb7018f9fd2220a6d901a9027f9abcf935372727cbf09ebd61a2a2eeb87653e8ecad1bab85dc8327"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "2020b78264a82d9f4151141adba8d44bf20c5ec062eee9b595a11f9e84901bf148f298e0c9f8777dcdbc7cc4670aac356cc2ad8ccb1629f16f6a76bcefbee760"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "d1b897b0e075ba68ab572adf9d9c436663e43eb3d8e62d92fc49c9be214e6f27873fe215a65170e6bea902408a25b49506f47babd07cecf7113ec10c5dd31252"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223242526",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "b14d0c62abfa469a357177e594c10c194243ed2025ab8aa5ad2fa41ad318e0ff48cd5e60bec07b13634a711d2326e488a985f31e31153399e73088efc86a5c55"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "4169c5cc808d2697dc2a82430dc23e3cd356dc70a94566810502b8d655b39abf9e7f902fe717e0389219859e1945df1af6ada42e4ccda55a197b7100a30c30a1"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "258a4edb113d66c839c8b1c91f15f35ade609f11cd7f8681a4045b9fef7b0b24c82cda06a5f2067b368825e3914e53d6948ede92efd6e8387fa2e537239b5bee"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223242526272829",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "79d2d8696d30f30fb34657761171a11e6c3f1e64cbe7bebee159cb95bfaf812b4f411e2f26d9c421dc2c284a3342d823ec293849e42d1e46b0a4ac1e3c86abaa"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "8b9436010dc5dee992ae38aea97f2cd63b946d94fedd2ec9671dcde3bd4ce9564d555c66c15bb2b900df72edb6b891ebcadfeff63c9ea4036a998be7973981e7"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "c8f68e696ed28242bf997f5b3b34959508e42d613810f1e2a435c96ed2ff560c7022f361a9234b9837feee90bf47922ee0fd5f8ddf823718d86d1e16c6090071"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "b02d3eee4860d5868b2c39ce39bfe81011290564dd678c85e8783f29302dfc1399ba95b6b53cd9ebbf400cca1db0ab67e19a325f2d115812d25d00978ad1bca4"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "7693ea73af3ac4dad21ca0d8da85b3118a7d1c6024cfaf557699868217bc0c2f44a199bc6c0edd519798ba05bd5b1b4484346a47c2cadf6bf30b785cc88b2baf"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "a0e5c1c0031c02e48b7f09a5e896ee9aef2f17fc9e18e997d7f6cac7ae316422c2b1e77984e5f3a73cb45deed5d3f84600105e6ee38f2d090c7d0442ea34c46d"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "41daa6adcfdb69f1440c37b596440165c15ada596813e2e22f060fcd551f24dee8e04ba6890387886ceec4a7a0d7fc6b44506392ec3822c0d8c1acfc7d5aebe8"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "14d4d40d5984d84c5cf7523b7798b254e275a3a8cc0a1bd06ebc0bee726856acc3cbf516ff667cda2058ad5c3412254460a82c92187041363cc77a4dc215e487"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "d0e7a1e2b9a447fee83e2277e9ff8010c2f375ae12fa7aaa8ca5a6317868a26a367a0b69fbc1cf32a55d34eb370663016f3d2110230eba754028a56f54acf57c"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "e771aa8db5a3e043e8178f39a0857ba04a3f18e4aa05743cf8d222b0b095825350ba422f63382a23d92e4149074e816a36c1cd28284d146267940b31f8818ea2"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "feb4fd6f9e87a56bef398b3284d2bda5b5b0e166583a66b61e538457ff0584872c21a32962b9928ffab58de4af2edd4e15d8b35570523207ff4e2a5aa7754caa"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "462f17bf005fb1c1b9e671779f665209ec2873e3e411f98dabf240a1d5ec3f95ce6796b6fc23fe171903b502023467dec7273ff74879b92967a2a43a5a183d33"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "d3338193b64553dbd38d144bea71c5915bb110e2d88180dbc5db364fd6171df317fc7268831b5aef75e4342b2fad8797ba39eddcef80e6ec08159350b1ad696d"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233343536",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "e1590d585a3d39f7cb599abd479070966409a6846d4377acf4471d065d5db94129cc9be92573b05ed226be1e9b7cb0cabe87918589f80dadd4ef5ef25a93d28e"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "f8f3726ac5a26cc80132493a6fedcb0e60760c09cfc84cad178175986819665e76842d7b9fedf76dddebf5d3f56faaad4477587af21606d396ae570d8e719af2"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "30186055c07949948183c850e9a756cc09937e247d9d928e869e20bafc3cd9721719d34e04a0899b92c736084550186886efba2e790d8be6ebf040b209c439a4"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233343536373839",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "f3c4276cb863637712c241c444c5cc1e3554e0fddb174d035819dd83eb700b4ce88df3ab3841ba02085e1a99b4e17310c5341075c0458ba376c95a6818fbb3e2"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "0aa007c4dd9d5832393040a1583c930bca7dc5e77ea53add7e2b3f7c8e231368043520d4a3ef53c969b6bbfd025946f632bd7f765d53c21003b8f983f75e2a6a"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "08e9464720533b23a04ec24f7ae8c103145f765387d738777d3d343477fd1c58db052142cab754ea674378e18766c53542f71970171cc4f81694246b717d7564"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "d37ff7ad297993e7ec21e0f1b4b5ae719cdc83c5db687527f27516cbffa822888a6810ee5c1ca7bfe3321119be1ab7bfa0a502671c8329494df7ad6f522d440f"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "dd9042f6e464dcf86b1262f6accfafbd8cfd902ed3ed89abf78ffa482dbdeeb6969842394c9a1168ae3d481a017842f660002d42447c6b22f7b72f21aae021c9"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "bd965bf31e87d70327536f2a341cebc4768eca275fa05ef98f7f1b71a0351298de006fba73fe6733ed01d75801b4a928e54231b38e38c562b2e33ea1284992fa"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "65676d800617972fbd87e4b9514e1c67402b7a331096d3bfac22f1abb95374abc942f16e9ab0ead33b87c91968a6e509e119ff07787b3ef483e1dcdccf6e3022"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "939fa189699c5d2c81ddd1ffc1fa207c970b6a3685bb29ce1d3e99d42f2f7442da53e95a72907314f4588399a3ff5b0a92beb3f6be2694f9f86ecf2952d5b41c"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f4041",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "c516541701863f91005f314108ceece3c643e04fc8c42fd2ff556220e616aaa6a48aeb97a84bad74782e8dff96a1a2fa949339d722edcaa32b57067041df88cc"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "987fd6e0d6857c553eaebb3d34970a2c2f6e89a3548f492521722b80a1c21a153892346d2cba6444212d56da9a26e324dccbc0dcde85d4d2ee4399eec5a64e8f"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "ae56deb1c2328d9c4017706bce6e99d41349053ba9d336d677c4c27d9fd50ae6aee17e853154e1f4fe7672346da2eaa31eea53fcf24a22804f11d03da6abfc2b"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f4041424344",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "49d6a608c9bde4491870498572ac31aac3fa40938b38a7818f72383eb040ad39532bc06571e13d767e6945ab77c0bdc3b0284253343f9f6c1244ebf2ff0df866"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "da582ad8c5370b4469af862aa6467a2293b2b28bd80ae0e91f425ad3d47249fdf98825cc86f14028c3308c9804c78bfeeeee461444ce243687e1a50522456a1d"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243444546",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "d5266aa3331194aef852eed86d7b5b2633a0af1c735906f2e13279f14931a9fc3b0eac5ce9245273bd1aa92905abe16278ef7efd47694789a7283b77da3c70f8"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f4041424344454647",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "2962734c28252186a9a1111c732ad4de4506d4b4480916303eb7991d659ccda07a9911914bc75c418ab7a4541757ad054796e26797feaf36e9f6ad43f14b35a4"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "e8b79ec5d06e111bdfafd71e9f5760f00ac8ac5d8bf768f9ff6f08b8f026096b1cc3a4c973333019f1e3553e77da3f98cb9f542e0a90e5f8a940cc58e59844b3"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243444546474849",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "dfb320c44f9d41d1efdcc015f08dd5539e526e39c87d509ae6812a969e5431bf4fa7d91ffd03b981e0d544cf72d7b1c0374f8801482e6dea2ef903877eba675e"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "d88675118fdb55a5fb365ac2af1d217bf526ce1ee9c94b2f0090b2c58a06ca58187d7fe57c7bed9d26fca067b4110eefcd9a0a345de872abe20de368001b0745"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "b893f2fc41f7b0dd6e2f6aa2e0370c0cff7df09e3acfcc0e920b6e6fad0ef747c40668417d342b80d2351e8c175f20897a062e9765e6c67b539b6ba8b9170545"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "6c67ec5697accd235c59b486d7b70baeedcbd4aa64ebd4eef3c7eac189561a726250aec4d48cadcafbbe2ce3c16ce2d691a8cce06e8879556d4483ed7165c063"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "f1aa2b044f8f0c638a3f362e677b5d891d6fd2ab0765f6ee1e4987de057ead357883d9b405b9d609eea1b869d97fb16d9b51017c553f3b93c0a1e0f1296fedcd"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "cbaa259572d4aebfc1917acddc582b9f8dfaa928a198ca7acd0f2aa76a134a90252e6298a65b08186a350d5b7626699f8cb721a3ea5921b753ae3a2dce24ba3a"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "fa1549c9796cd4d303dcf452c1fbd5744fd9b9b47003d920b92de34839d07ef2a29ded68f6fc9e6c45e071a2e48bd50c5084e96b657dd0404045a1ddefe282ed"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f50",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "5cf2ac897ab444dcb5c8d87c495dbdb34e1838b6b629427caa51702ad0f9688525f13bec503a3c3a2c80a65e0b5715e8afab00ffa56ec455a49a1ad30aa24fcd"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f5051",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "9aaf80207bace17bb7ab145757d5696bde32406ef22b44292ef65d4519c3bb2ad41a59b62cc3e94b6fa96d32a7faadae28af7d35097219aa3fd8cda31e40c275"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "af88b163402c86745cb650c2988fb95211b94b03ef290eed9662034241fd51cf398f8073e369354c43eae1052f9b63b08191caa138aa54fea889cc7024236897"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f50515253",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "48fa7d64e1ceee27b9864db5ada4b53d00c9bc7626555813d3cd6730ab3cc06ff342d727905e33171bde6e8476e77fb1720861e94b73a2c538d254746285f430"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f5051525354",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "0e6fd97a85e904f87bfe85bbeb34f69e1f18105cf4ed4f87aec36c6e8b5f68bd2a6f3dc8a9ecb2b61db4eedb6b2ea10bf9cb0251fb0f8b344abf7f366b6de5ab"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "06622da5787176287fdc8fed440bad187d830099c94e6d04c8e9c954cda70c8bb9e1fc4a6d0baa831b9b78ef6648681a4867a11da93ee36e5e6a37d87fc63f6f"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f50515253545556",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "1da6772b58fabf9c61f68d412c82f182c0236d7d575ef0b58dd22458d643cd1dfc93b03871c316d8430d312995d4197f0874c99172ba004a01ee295abac24e46"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f5051525354555657",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "3cd2d9320b7b1d5fb9aab951a76023fa667be14a9124e394513918a3f44096ae4904ba0ffc150b63bc7ab1eeb9a6e257e5c8f000a70394a5afd842715de15f29"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "04cdc14f7434e0b4be70cb41db4c779a88eaef6accebcb41f2d42fffe7f32a8e281b5c103a27021d0d08362250753cdf70292195a53a48728ceb5844c2d98bab"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f50515253545556575859",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "9071b7a8a075d0095b8fb3ae5113785735ab98e2b52faf91d5b89e44aac5b5d4ebbf91223b0ff4c71905da55342e64655d6ef8c89a4768c3f93a6dc0366b5bc8"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "ebb30240dd96c7bc8d0abe49aa4edcbb4afdc51ff9aaf720d3f9e7fbb0f9c6d6571350501769fc4ebd0b2141247ff400d4fd4be414edf37757bb90a32ac5c65a"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "8532c58bf3c8015d9d1cbe00eef1f5082f8f3632fbe9f1ed4f9dfb1fa79e8283066d77c44c4af943d76b300364aecbd0648c8a8939bd204123f4b56260422dec"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "fe9846d64f7c7708696f840e2d76cb4408b6595c2f81ec6a28a7f2f20cb88cfe6ac0b9e9b8244f08bd7095c350c1d0842f64fb01bb7f532dfcd47371b0aeeb79"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "28f17ea6fb6c42092dc264257e29746321fb5bdaea9873c2a7fa9d8f53818e899e161bc77dfe8090afd82bf2266c5c1bc930a8d1547624439e662ef695f26f24"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "ec6b7d7f030d4850acae3cb615c21dd25206d63e84d1db8d957370737ba0e98467ea0ce274c66199901eaec18a08525715f53bfdb0aacb613d342ebdceeddc3b"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "b403d3691c03b0d3418df327d5860d34bbfcc4519bfbce36bf33b208385fadb9186bc78a76c489d89fd57e7dc75412d23bcd1dae8470ce9274754bb8585b13c5"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f60",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "31fc79738b8772b3f55cd8178813b3b52d0db5a419d30ba9495c4b9da0219fac6df8e7c23a811551a62b827f256ecdb8124ac8a6792ccfecc3b3012722e94463"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f6061",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "bb2039ec287091bcc9642fc90049e73732e02e577e2862b32216ae9bedcd730c4c284ef3968c368b7d37584f97bd4b4dc6ef6127acfe2e6ae2509124e66c8af4"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "f53d68d13f45edfcb9bd415e2831e938350d5380d3432278fc1c0c381fcb7c65c82dafe051d8c8b0d44e0974a0e59ec7bf7ed0459f86e96f329fc79752510fd3"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f60616263",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "8d568c7984f0ecdf7640fbc483b5d8c9f86634f6f43291841b309a350ab9c1137d24066b09da9944bac54d5bb6580d836047aac74ab724b887ebf93d4b32eca9"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f6061626364",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "c0b65ce5a96ff774c456cac3b5f2c4cd359b4ff53ef93a3da0778be4900d1e8da1601e769e8f1b02d2a2f8c5b9fa10b44f1c186985468feeb008730283a6657d"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "4900bba6f5fb103ece8ec96ada13a5c3c85488e05551da6b6b33d988e611ec0fe2e3c2aa48ea6ae8986a3a231b223c5d27cec2eadde91ce07981ee652862d1e4"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f60616263646566",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "c7f5c37c7285f927f76443414d4357ff789647d7a005a5a787e03c346b57f49f21b64fa9cf4b7e45573e23049017567121a9c3d4b2b73ec5e9413577525db45a"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f6061626364656667",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "ec7096330736fdb2d64b5653e7475da746c23a4613a82687a28062d3236364284ac01720ffb406cfe265c0df626a188c9e5963ace5d3d5bb363e32c38c2190a6"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "82e744c75f4649ec52b80771a77d475a3bc091989556960e276a5f9ead92a03f718742cdcfeaee5cb85c44af198adc43a4a428f5f0c2ddb0be36059f06d7df73"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f60616263646566676869",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "2834b7a7170f1f5b68559ab78c1050ec21c919740b784a9072f6e5d69f828d70c919c5039fb148e39e2c8a52118378b064ca8d5001cd10a5478387b966715ed6"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "16b4ada883f72f853bb7ef253efcab0c3e2161687ad61543a0d2824f91c1f81347d86be709b16996e17f2dd486927b0288ad38d13063c4a9672c39397d3789b6"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "78d048f3a69d8b54ae0ed63a573ae350d89f7c6cf1f3688930de899afa037697629b314e5cd303aa62feea72a25bf42b304b6c6bcb27fae21c16d925e1fbdac3"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "0f746a48749287ada77a82961f05a4da4abdb7d77b1220f836d09ec814359c0ec0239b8c7b9ff9e02f569d1b301ef67c4612d1de4f730f81c12c40cc063c5caa"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "f0fc859d3bd195fbdc2d591e4cdac15179ec0f1dc821c11df1f0c1d26e6260aaa65b79fafacafd7d3ad61e600f250905f5878c87452897647a35b995bcadc3a3"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "2620f687e8625f6a412460b42e2cef67634208ce10a0cbd4dff7044a41b7880077e9f8dc3b8d1216d3376a21e015b58fb279b521d83f9388c7382c8505590b9b"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "227e3aed8d2cb10b918fcb04f9de3e6d0a57e08476d93759cd7b2ed54a1cbf0239c528fb04bbf288253e601d3bc38b21794afef90b17094a182cac557745e75f"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f70",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "1a929901b09c25f27d6b35be7b2f1c4745131fdebca7f3e2451926720434e0db6e74fd693ad29b777dc3355c592a361c4873b01133a57c2e3b7075cbdb86f4fc"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f7071",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "5fd7968bc2fe34f220b5e3dc5af9571742d73b7d60819f2888b629072b96a9d8ab2d91b82d0a9aaba61bbd39958132fcc4257023d1eca591b3054e2dc81c8200"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "dfcce8cf32870cc6a503eadafc87fd6f78918b9b4d0737db6810be996b5497e7e5cc80e312f61e71ff3e9624436073156403f735f56b0b01845c18f6caf772e6"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f70717273",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "02f7ef3a9ce0fff960f67032b296efca3061f4934d690749f2d01c35c81c14f39a67fa350bc8a0359bf1724bffc3bca6d7c7bba4791fd522a3ad353c02ec5aa8"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f7071727374",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "64be5c6aba65d594844ae78bb022e5bebe127fd6b6ffa5a13703855ab63b624dcd1a363f99203f632ec386f3ea767fc992e8ed9686586aa27555a8599d5b808f"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "f78585505c4eaa54a8b5be70a61e735e0ff97af944ddb3001e35d86c4e2199d976104b6ae31750a36a726ed285064f5981b503889fef822fcdc2898dddb7889a"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f70717273747576",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "e4b5566033869572edfd87479a5bb73c80e8759b91232879d96b1dda36c012076ee5a2ed7ae2de63ef8406a06aea82c188031b560beafb583fb3de9e57952a7e"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f7071727374757677",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "e1b3e7ed867f6c9484a2a97f7715f25e25294e992e41f6a7c161ffc2adc6daaeb7113102d5e6090287fe6ad94ce5d6b739c6ca240b05c76fb73f25dd024bf935"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "85fd085fdc12a080983df07bd7012b0d402a0f4043fcb2775adf0bad174f9b08d1676e476985785c0a5dcc41dbff6d95ef4d66a3fbdc4a74b82ba52da0512b74"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f70717273747576777879",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "aed8fa764b0fbff821e05233d2f7b0900ec44d826f95e93c343c1bc3ba5a24374b1d616e7e7aba453a0ada5e4fab5382409e0d42ce9c2bc7fb39a99c340c20f0"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "7ba3b2e297233522eeb343bd3ebcfd835a04007735e87f0ca300cbee6d416565162171581e4020ff4cf176450f1291ea2285cb9ebffe4c56660627685145051c"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "de748bcf89ec88084721e16b85f30adb1a6134d664b5843569babc5bbd1a15ca9b61803c901a4fef32965a1749c9f3a4e243e173939dc5a8dc495c671ab52145"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "aaf4d2bdf200a919706d9842dce16c98140d34bc433df320aba9bd429e549aa7a3397652a4d768277786cf993cde2338673ed2e6b66c961fefb82cd20c93338f"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "c408218968b788bf864f0997e6bc4c3dba68b276e2125a4843296052ff93bf5767b8cdce7131f0876430c1165fec6c4f47adaa4fd8bcfacef463b5d3d0fa61a0"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "76d2d819c92bce55fa8e092ab1bf9b9eab237a25267986cacf2b8ee14d214d730dc9a5aa2d7b596e86a1fd8fa0804c77402d2fcd45083688b218b1cdfa0dcbcb"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "72065ee4dd91c2d8509fa1fc28a37c7fc9fa7d5b3f8ad3d0d7a25626b57b1b44788d4caf806290425f9890a3a2a35a905ab4b37acfd0da6e4517b2525c9651e4"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f80",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "64475dfe7600d7171bea0b394e27c9b00d8e74dd1e416a79473682ad3dfdbb706631558055cfc8a40e07bd015a4540dcdea15883cbbf31412df1de1cd4152b91"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f8081",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "12cd1674a4488a5d7c2b3160d2e2c4b58371bedad793418d6f19c6ee385d70b3e06739369d4df910edb0b0a54cbff43d54544cd37ab3a06cfa0a3ddac8b66c89"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "60756966479dedc6dd4bcff8ea7d1d4ce4d4af2e7b097e32e3763518441147cc12b3c0ee6d2ecabf1198cec92e86a3616fba4f4e872f5825330adbb4c1dee444"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f80818283",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "a7803bcb71bc1d0f4383dde1e0612e04f872b715ad30815c2249cf34abb8b024915cb2fc9f4e7cc4c8cfd45be2d5a91eab0941c7d270e2da4ca4a9f7ac68663a"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f8081828384",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "b84ef6a7229a34a750d9a98ee2529871816b87fbe3bc45b45fa5ae82d5141540211165c3c5d7a7476ba5a4aa06d66476f0d9dc49a3f1ee72c3acabd498967414"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "fae4b6d8efc3f8c8e64d001dabec3a21f544e82714745251b2b4b393f2f43e0da3d403c64db95a2cb6e23ebb7b9e94cdd5ddac54f07c4a61bd3cb10aa6f93b49"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f80818283848586",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "34f7286605a122369540141ded79b8957255da2d4155abbf5a8dbb89c8eb7ede8eeef1daa46dc29d751d045dc3b1d658bb64b80ff8589eddb3824b13da235a6b"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f8081828384858687",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "3b3b48434be27b9eababba43bf6b35f14b30f6a88dc2e750c358470d6b3aa3c18e47db4017fa55106d8252f016371a00f5f8b070b74ba5f23cffc5511c9f09f0"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "ba289ebd6562c48c3e10a8ad6ce02e73433d1e93d7c9279d4d60a7e879ee11f441a000f48ed9f7c4ed87a45136d7dccdca482109c78a51062b3ba4044ada2469"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f80818283848586878889",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "022939e2386c5a37049856c850a2bb10a13dfea4212b4c732a8840a9ffa5faf54875c5448816b2785a007da8a8d2bc7d71a54e4e6571f10b600cbdb25d13ede3"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "e6fec19d89ce8717b1a087024670fe026f6c7cbda11caef959bb2d351bf856f8055d1c0ebdaaa9d1b17886fc2c562b5e99642fc064710c0d3488a02b5ed7f6fd"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "94c96f02a8f576aca32ba61c2b206f907285d9299b83ac175c209a8d43d53bfe683dd1d83e7549cb906c28f59ab7c46f8751366a28c39dd5fe2693c9019666c8"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "31a0cd215ebd2cb61de5b9edc91e6195e31c59a5648d5c9f737e125b2605708f2e325ab3381c8dce1a3e958886f1ecdc60318f882cfe20a24191352e617b0f21"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "91ab504a522dce78779f4c6c6ba2e6b6db5565c76d3e7e7c920caf7f757ef9db7c8fcf10e57f03379ea9bf75eb59895d96e149800b6aae01db778bb90afbc989"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "d85cabc6bd5b1a01a5afd8c6734740da9fd1c1acc6db29bfc8a2e5b668b028b6b3154bfb8703fa3180251d589ad38040ceb707c4bad1b5343cb426b61eaa49c1"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "d62efbec2ca9c1f8bd66ce8b3f6a898cb3f7566ba6568c618ad1feb2b65b76c3ce1dd20f7395372faf28427f61c9278049cf0140df434f5633048c86b81e0399"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f90",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "7c8fdc6175439e2c3db15bafa7fb06143a6a23bc90f449e79deef73c3d492a671715c193b6fea9f036050b946069856b897e08c00768f5ee5ddcf70b7cd6d0e0"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f9091",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "58602ee7468e6bc9df21bd51b23c005f72d6cb013f0a1b48cbec5eca299299f97f09f54a9a01483eaeb315a6478bad37ba47ca1347c7c8fc9e6695592c91d723"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "27f5b79ed256b050993d793496edf4807c1d85a7b0a67c9c4fa99860750b0ae66989670a8ffd7856d7ce411599e58c4d77b232a62bef64d15275be46a68235ff"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f90919293",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "3957a976b9f1887bf004a8dca942c92d2b37ea52600f25e0c9bc5707d0279c00c6e85a839b0d2d8eb59c51d94788ebe62474a791cadf52cccf20f5070b6573fc"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f9091929394",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "eaa2376d55380bf772ecca9cb0aa4668c95c707162fa86d518c8ce0ca9bf7362b9f2a0adc3ff59922df921b94567e81e452f6c1a07fc817cebe99604b3505d38"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "c1e2c78b6b2734e2480ec550434cb5d613111adcc21d475545c3b1b7e6ff12444476e5c055132e2229dc0f807044bb919b1a5662dd38a9ee65e243a3911aed1a"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f90919293949596",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "8ab48713389dd0fcf9f965d3ce66b1e559a1f8c58741d67683cd971354f452e62d0207a65e436c5d5d8f8ee71c6abfe50e669004c302b31a7ea8311d4a916051"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f9091929394959697",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "24ce0addaa4c65038bd1b1c0f1452a0b128777aabc94a29df2fd6c7e2f85f8ab9ac7eff516b0e0a825c84a24cfe492eaad0a6308e46dd42fe8333ab971bb30ca"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "5154f929ee03045b6b0c0004fa778edee1d139893267cc84825ad7b36c63de32798e4a166d24686561354f63b00709a1364b3c241de3febf0754045897467cd4"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f90919293949596979899",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "e74e907920fd87bd5ad636dd11085e50ee70459c443e1ce5809af2bc2eba39f9e6d7128e0e3712c316da06f4705d78a4838e28121d4344a2c79c5e0db307a677"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "bf91a22334bac20f3fd80663b3cd06c4e8802f30e6b59f90d3035cc9798a217ed5a31abbda7fa6842827bdf2a7a1c21f6fcfccbb54c6c52926f32da816269be1"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "d9d5c74be5121b0bd742f26bffb8c89f89171f3f934913492b0903c271bbe2b3395ef259669bef43b57f7fcc3027db01823f6baee66e4f9fead4d6726c741fce"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "50c8b8cf34cd879f80e2faab3230b0c0e1cc3e9dcadeb1b9d97ab923415dd9a1fe38addd5c11756c67990b256e95ad6d8f9fedce10bf1c90679cde0ecf1be347"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "0a386e7cd5dd9b77a035e09fe6fee2c8ce61b5383c87ea43205059c5e4cd4f4408319bb0a82360f6a58e6c9ce3f487c446063bf813bc6ba535e17fc1826cfc91"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "1f1459cb6b61cbac5f0efe8fc487538f42548987fcd56221cfa7beb22504769e792c45adfb1d6b3d60d7b749c8a75b0bdf14e8ea721b95dca538ca6e25711209"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "e58b3836b7d8fedbb50ca5725c6571e74c0785e97821dab8b6298c10e4c079d4a6cdf22f0fedb55032925c16748115f01a105e77e00cee3d07924dc0d8f90659"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "b929cc6505f020158672deda56d0db081a2ee34c00c1100029bdf8ea98034fa4bf3e8655ec697fe36f40553c5bb46801644a627d3342f4fc92b61f03290fb381"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "72d353994b49d3e03153929a1e4d4f188ee58ab9e72ee8e512f29bc773913819ce057ddd7002c0433ee0a16114e3d156dd2c4a7e80ee53378b8670f23e33ef56"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "c70ef9bfd775d408176737a0736d68517ce1aaad7e81a93c8c1ed967ea214f56c8a377b1763e676615b60f3988241eae6eab9685a5124929d28188f29eab06f7"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "c230f0802679cb33822ef8b3b21bf7a9a28942092901d7dac3760300831026cf354c9232df3e084d9903130c601f63c1f4a4a4b8106e468cd443bbe5a734f45f"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "6f43094cafb5ebf1f7a4937ec50f56a4c9da303cbb55ac1f27f1f1976cd96beda9464f0e7b9c54620b8a9fba983164b8be3578425a024f5fe199c36356b88972"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "3745273f4c38225db2337381871a0c6aafd3af9b018c88aa02025850a5dc3a42a1a3e03e56cbf1b0876d63a441f1d2856a39b8801eb5af325201c415d65e97fe"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "c50c44cca3ec3edaae779a7e179450ebdda2f97067c690aa6c5a4ac7c30139bb27c0df4db3220e63cb110d64f37ffe078db72653e2daacf93ae3f0a2d1a7eb2e"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "8aef263e385cbc61e19b28914243262af5afe8726af3ce39a79c27028cf3ecd3f8d2dfd9cfc9ad91b58f6f20778fd5f02894a3d91c7d57d1e4b866a7f364b6be"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "28696141de6e2d9bcb3235578a66166c1448d3e905a1b482d423be4bc5369bc8c74dae0acc9cc123e1d8ddce9f97917e8c019c552da32d39d2219b9abf0fa8c8"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "2fb9eb2085830181903a9dafe3db428ee15be7662224efd643371fb25646aee716e531eca69b2bdc8233f1a8081fa43da1500302975a77f42fa592136710e9dc"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aa",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "66f9a7143f7a3314a669bf2e24bbb35014261d639f495b6c9c1f104fe8e320aca60d4550d69d52edbd5a3cdeb4014ae65b1d87aa770b69ae5c15f4330b0b0ad8"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaab",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "f4c4dd1d594c3565e3e25ca43dad82f62abea4835ed4cd811bcd975e46279828d44d4c62c3679f1b7f7b9dd4571d7b49557347b8c5460cbdc1bef690fb2a08c0"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabac",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "8f1dc9649c3a84551f8f6e91cac68242a43b1f8f328ee92280257387fa7559aa6db12e4aeadc2d26099178749c6864b357f3f83b2fb3efa8d2a8db056bed6bcc"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacad",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "3139c1a7f97afd1675d460ebbc07f2728aa150df849624511ee04b743ba0a833092f18c12dc91b4dd243f333402f59fe28abdbbbae301e7b659c7a26d5c0f979"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadae",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "06f94a2996158a819fe34c40de3cf0379fd9fb85b3e363ba3926a0e7d960e3f4c2e0c70c7ce0ccb2a64fc29869f6e7ab12bd4d3f14fce943279027e785fb5c29"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "c29c399ef3eee8961e87565c1ce263925fc3d0ce267d13e48dd9e732ee67b0f69fad56401b0f10fcaac119201046cca28c5b14abdea3212ae65562f7f138db3d"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "4cec4c9df52eef05c3f6faaa9791bc7445937183224ecc37a1e58d0132d35617531d7e795f52af7b1eb9d147de1292d345fe341823f8e6bc1e5badca5c656108"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "898bfbae93b3e18d00697eab7d9704fa36ec339d076131cefdf30edbe8d9cc81c3a80b129659b163a323bab9793d4feed92d54dae966c77529764a09be88db45"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "ee9bd0469d3aaf4f14035be48a2c3b84d9b4b1fff1d945e1f1c1d38980a951be197b25fe22c731f20aeacc930ba9c4a1f4762227617ad350fdabb4e80273a0f4"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "3d4d3113300581cd96acbf091c3d0f3c310138cd6979e6026cde623e2dd1b24d4a8638bed1073344783ad0649cc6305ccec04beb49f31c633088a99b65130267"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "95c0591ad91f921ac7be6d9ce37e0663ed8011c1cfd6d0162a5572e94368bac02024485e6a39854aa46fe38e97d6c6b1947cd272d86b06bb5b2f78b9b68d559d"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "227b79ded368153bf46c0a3ca978bfdbef31f3024a5665842468490b0ff748ae04e7832ed4c9f49de9b1706709d623e5c8c15e3caecae8d5e433430ff72f20eb"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "5d34f3952f0105eef88ae8b64c6ce95ebfade0e02c69b08762a8712d2e4911ad3f941fc4034dc9b2e479fdbcd279b902faf5d838bb2e0c6495d372b5b7029813"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "7f939bf8353abce49e77f14f3750af20b7b03902e1a1e7fb6aaf76d0259cd401a83190f15640e74f3e6c5a90e839c7821f6474757f75c7bf9002084ddc7a62dc"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "062b61a2f9a33a71d7d0a06119644c70b0716a504de7e5e1be49bd7b86e7ed6817714f9f0fc313d06129597e9a2235ec8521de36f7290a90ccfc1ffa6d0aee29"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "f29e01eeae64311eb7f1c6422f946bf7bea36379523e7b2bbaba7d1d34a22d5ea5f1c5a09d5ce1fe682cced9a4798d1a05b46cd72dff5c1b355440b2a2d476bc"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9ba",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "ec38cd3bbab3ef35d7cb6d5c914298351d8a9dc97fcee051a8a02f58e3ed6184d0b7810a5615411ab1b95209c3c810114fdeb22452084e77f3f847c6dbaafe16"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babb",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "c2aef5e0ca43e82641565b8cb943aa8ba53550caef793b6532fafad94b816082f0113a3ea2f63608ab40437ecc0f0229cb8fa224dcf1c478a67d9b64162b92d1"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbc",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "15f534efff7105cd1c254d074e27d5898b89313b7d366dc2d7d87113fa7d53aae13f6dba487ad8103d5e854c91fdb6e1e74b2ef6d1431769c30767dde067a35c"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbd",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "89acbca0b169897a0a2714c2df8c95b5b79cb69390142b7d6018bb3e3076b099b79a964152a9d912b1b86412b7e372e9cecad7f25d4cbab8a317be36492a67d7"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbe",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "e3c0739190ed849c9c962fd9dbb55e207e624fcac1eb417691515499eea8d8267b7e8f1287a63633af5011fde8c4ddf55bfdf722edf88831414f2cfaed59cb9a"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "8d6cf87c08380d2d1506eee46fd4222d21d8c04e585fbfd08269c98f702833a156326a0724656400ee09351d57b440175e2a5de93cc5f80db6daf83576cf75fa"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "da24bede383666d563eeed37f6319baf20d5c75d1635a6ba5ef4cfa1ac95487e96f8c08af600aab87c986ebad49fc70a58b4890b9c876e091016daf49e1d322e"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "f9d1d1b1e87ea7ae753a029750cc1cf3d0157d41805e245c5617bb934e732f0ae3180b78e05bfe76c7c3051e3e3ac78b9b50c05142657e1e03215d6ec7bfd0fc"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "11b7bc1668032048aa43343de476395e814bbbc223678db951a1b03a021efac948cfbe215f97fe9a72a2f6bc039e3956bfa417c1a9f10d6d7ba5d3d32ff323e5"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "b8d9000e4fc2b066edb91afee8e7eb0f24e3a201db8b6793c0608581e628ed0bcc4e5aa6787992a4bcc44e288093e63ee83abd0bc3ec6d0934a674a4da13838a"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "ce325e294f9b6719d6b61278276ae06a2564c03bb0b783fafe785bdf89c7d5acd83e78756d301b445699024eaeb77b54d477336ec2a4f332f2b3f88765ddb0c3"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "29acc30e9603ae2fccf90bf97e6cc463ebe28c1b2f9b4b765e70537c25c702a29dcbfbf14c99c54345ba2b51f17b77b5f15db92bbad8fa95c471f5d070a137cc"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "3379cbaae562a87b4c0425550ffdd6bfe1203f0d666cc7ea095be407a5dfe61ee91441cd5154b3e53b4f5fb31ad4c7a9ad5c7af4ae679aa51a54003a54ca6b2d"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "3095a349d245708c7cf550118703d7302c27b60af5d4e67fc978f8a4e60953c7a04f92fcf41aee64321ccb707a895851552b1e37b00bc5e6b72fa5bcef9e3fff"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "07262d738b09321f4dbccec4bb26f48cb0f0ed246ce0b31b9a6e7bc683049f1f3e5545f28ce932dd985c5ab0f43bd6de0770560af329065ed2e49d34624c2cbb"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "b6405eca8ee3316c87061cc6ec18dba53e6c250c63ba1f3bae9e55dd3498036af08cd272aa24d713c6020d77ab2f3919af1a32f307420618ab97e73953994fb4"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9ca",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "7ee682f63148ee45f6e5315da81e5c6e557c2c34641fc509c7a5701088c38a74756168e2cd8d351e88fd1a451f360a01f5b2580f9b5a2e8cfc138f3dd59a3ffc"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacb",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "1d263c179d6b268f6fa016f3a4f29e943891125ed8593c81256059f5a7b44af2dcb2030d175c00e62ecaf7ee96682aa07ab20a611024a28532b1c25b86657902"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcc",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "106d132cbdb4cd2597812846e2bc1bf732fec5f0a5f65dbb39ec4e6dc64ab2ce6d24630d0f15a805c3540025d84afa98e36703c3dbee713e72dde8465bc1be7e"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccd",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "0e79968226650667a8d862ea8da4891af56a4e3a8b6d1750e394f0dea76d640d85077bcec2cc86886e506751b4f6a5838f7f0b5fef765d9dc90dcdcbaf079f08"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdce",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "521156a82ab0c4e566e5844d5e31ad9aaf144bbd5a464fdca34dbd5717e8ff711d3ffebbfa085d67fe996a34f6d3e4e60b1396bf4b1610c263bdbb834d560816"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecf",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "1aba88befc55bc25efbce02db8b9933e46f57661baeabeb21cc2574d2a518a3cba5dc5a38e49713440b25f9c744e75f6b85c9d8f4681f676160f6105357b8406"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "5a9949fcb2c473cda968ac1b5d08566dc2d816d960f57e63b898fa701cf8ebd3f59b124d95bfbbedc5f1cf0e17d5eaed0c02c50b69d8a402cabcca4433b51fd4"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "b0cead09807c672af2eb2b0f06dde46cf5370e15a4096b1a7d7cbb36ec31c205fbefca00b7a4162fa89fb4fb3eb78d79770c23f44e7206664ce3cd931c291e5d"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "bb6664931ec97044e45b2ae420ae1c551a8874bc937d08e969399c3964ebdba8346cdd5d09caafe4c28ba7ec788191ceca65ddd6f95f18583e040d0f30d0364d"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "65bc770a5faa3792369803683e844b0be7ee96f29f6d6a35568006bd5590f9a4ef639b7a8061c7b0424b66b60ac34af3119905f33a9d8c3ae18382ca9b689900"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "ea9b4dca333336aaf839a45c6eaa48b8cb4c7ddabffea4f643d6357ea6628a480a5b45f2b052c1b07d1fedca918b6f1139d80f74c24510dcbaa4be70eacc1b06"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "e6342fb4a780ad975d0e24bce149989b91d360557e87994f6b457b895575cc02d0c15bad3ce7577f4c63927ff13f3e381ff7e72bdbe745324844a9d27e3f1c01"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "3e209c9b33e8e461178ab46b1c64b49a07fb745f1c8bc95fbfb94c6b87c69516651b264ef980937fad41238b91ddc011a5dd777c7efd4494b4b6ecd3a9c22ac0"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "fd6a3d5b1875d80486d6e69694a56dbb04a99a4d051f15db2689776ba1c4882e6d462a603b7015dc9f4b7450f05394303b8652cfb404a266962c41bae6e18a94"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "951e27517e6bad9e4195fc8671dee3e7e9be69cee1422cb9fecfce0dba875f7b310b93ee3a3d558f941f635f668ff832d2c1d033c5e2f0997e4c66f147344e02"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "8eba2f874f1ae84041903c7c4253c82292530fc8509550bfdc34c95c7e2889d5650b0ad8cb988e5c4894cb87fbfbb19612ea93ccc4c5cad17158b9763464b492"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9da",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "16f712eaa1b7c6354719a8e7dbdfaf55e4063a4d277d947550019b38dfb564830911057d50506136e2394c3b28945cc964967d54e3000c2181626cfb9b73efd2"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadb",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "c39639e7d5c7fb8cdd0fd3e6a52096039437122f21c78f1679cea9d78a734c56ecbeb28654b4f18e342c331f6f7229ec4b4bc281b2d80a6eb50043f31796c88c"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdc",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "72d081af99f8a173dcc9a0ac4eb3557405639a29084b54a40172912a2f8a395129d5536f0918e902f9e8fa6000995f4168ddc5f893011be6a0dbc9b8a1a3f5bb"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdd",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "c11aa81e5efd24d5fc27ee586cfd8847fbb0e27601ccece5ecca0198e3c7765393bb74457c7e7a27eb9170350e1fb53857177506be3e762cc0f14d8c3afe9077"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcddde",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "c28f2150b452e6c0c424bcde6f8d72007f9310fed7f2f87de0dbb64f4479d6c1441ba66f44b2accee61609177ed340128b407ecec7c64bbe50d63d22d8627727"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "f63d88122877ec30b8c8b00d22e89000a966426112bd44166e2f525b769ccbe9b286d437a0129130dde1a86c43e04bedb594e671d98283afe64ce331de9828fd"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "348b0532880b88a6614a8d7408c3f913357fbb60e995c60205be9139e74998aede7f4581e42f6b52698f7fa1219708c14498067fd1e09502de83a77dd281150c"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "5133dc8bef725359dff59792d85eaf75b7e1dcd1978b01c35b1b85fcebc63388ad99a17b6346a217dc1a9622ebd122ecf6913c4d31a6b52a695b86af00d741a0"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "2753c4c0e98ecad806e88780ec27fccd0f5c1ab547f9e4bf1659d192c23aa2cc971b58b6802580baef8adc3b776ef7086b2545c2987f348ee3719cdef258c403"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "b1663573ce4b9d8caefc865012f3e39714b9898a5da6ce17c25a6a47931a9ddb9bbe98adaa553beed436e89578455416c2a52a525cf2862b8d1d49a2531b7391"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "64f58bd6bfc856f5e873b2a2956ea0eda0d6db0da39c8c7fc67c9f9feefcff3072cdf9e6ea37f69a44f0c61aa0da3693c2db5b54960c0281a088151db42b11e8"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "0764c7be28125d9065c4b98a69d60aede703547c66a12e17e1c618994132f5ef82482c1e3fe3146cc65376cc109f0138ed9a80e49f1f3c7d610d2f2432f20605"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "f748784398a2ff03ebeb07e155e66116a839741a336e32da71ec696001f0ad1b25cd48c69cfca7265eca1dd71904a0ce748ac4124f3571076dfa7116a9cf00e9"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "3f0dbc0186bceb6b785ba78d2a2a013c910be157bdaffae81bb6663b1a73722f7f1228795f3ecada87cf6ef0078474af73f31eca0cc200ed975b6893f761cb6d"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "d4762cd4599876ca75b2b8fe249944dbd27ace741fdab93616cbc6e425460feb51d4e7adcc38180e7fc47c89024a7f56191adb878dfde4ead62223f5a2610efe"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "cd36b3d5b4c91b90fcbba79513cfee1907d8645a162afd0cd4cf4192d4a5f4c892183a8eacdb2b6b6a9d9aa8c11ac1b261b380dbee24ca468f1bfd043c58eefe"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9ea",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "98593452281661a53c48a9d8cd790826c1a1ce567738053d0bee4a91a3d5bd92eefdbabebe3204f2031ca5f781bda99ef5d8ae56e5b04a9e1ecd21b0eb05d3e1"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaeb",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "771f57dd2775ccdab55921d3e8e30ccf484d61fe1c1b9c2ae819d0fb2a12fab9be70c4a7a138da84e8280435daade5bbe66af0836a154f817fb17f3397e725a3"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebec",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "c60897c6f828e21f16fbb5f15b323f87b6c8955eabf1d38061f707f608abdd993fac3070633e286cf8339ce295dd352df4b4b40b2f29da1dd50b3a05d079e6bb"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebeced",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "8210cd2c2d3b135c2cf07fa0d1433cd771f325d075c6469d9c7f1ba0943cd4ab09808cabf4acb9ce5bb88b498929b4b847f681ad2c490d042db2aec94214b06b"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedee",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "1d4edfffd8fd80f7e4107840fa3aa31e32598491e4af7013c197a65b7f36dd3ac4b478456111cd4309d9243510782fa31b7c4c95fa951520d020eb7e5c36e4ef"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "af8e6e91fab46ce4873e1a50a8ef448cc29121f7f74deef34a71ef89cc00d9274bc6c2454bbb3230d8b2ec94c62b1dec85f3593bfa30ea6f7a44d7c09465a253"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "29fd384ed4906f2d13aa9fe7af905990938bed807f1832454a372ab412eea1f5625a1fcc9ac8343b7c67c5aba6e0b1cc4644654913692c6b39eb9187ceacd3ec"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "a268c7885d9874a51c44dffed8ea53e94f78456e0b2ed99ff5a3924760813826d960a15edbedbb5de5226ba4b074e71b05c55b9756bb79e55c02754c2c7b6c8a"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "0cf8545488d56a86817cd7ecb10f7116b7ea530a45b6ea497b6c72c997e09e3d0da8698f46bb006fc977c2cd3d1177463ac9057fdd1662c85d0c126443c10473"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "b39614268fdd8781515e2cfebf89b4d5402bab10c226e6344e6b9ae000fb0d6c79cb2f3ec80e80eaeb1980d2f8698916bd2e9f747236655116649cd3ca23a837"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "74bef092fc6f1e5dba3663a3fb003b2a5ba257496536d99f62b9d73f8f9eb3ce9ff3eec709eb883655ec9eb896b9128f2afc89cf7d1ab58a72f4a3bf034d2b4a"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "3a988d38d75611f3ef38b8774980b33e573b6c57bee0469ba5eed9b44f29945e7347967fba2c162e1c3be7f310f2f75ee2381e7bfd6b3f0baea8d95dfb1dafb1"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "58aedfce6f67ddc85a28c992f1c0bd0969f041e66f1ee88020a125cbfcfebcd61709c9c4eba192c15e69f020d462486019fa8dea0cd7a42921a19d2fe546d43d"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "9347bd291473e6b4e368437b8e561e065f649a6d8ada479ad09b1999a8f26b91cf6120fd3bfe014e83f23acfa4c0ad7b3712b2c3c0733270663112ccd9285cd9"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "b32163e7c5dbb5f51fdc11d2eac875efbbcb7e7699090a7e7ff8a8d50795af5d74d9ff98543ef8cdf89ac13d0485278756e0ef00c817745661e1d59fe38e7537"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "1085d78307b1c4b008c57a2e7e5b234658a0a82e4ff1e4aaac72b312fda0fe27d233bc5b10e9cc17fdc7697b540c7d95eb215a19a1a0e20e1abfa126efd568c7"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fa",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "4e5c734c7dde011d83eac2b7347b373594f92d7091b9ca34cb9c6f39bdf5a8d2f134379e16d822f6522170ccf2ddd55c84b9e6c64fc927ac4cf8dfb2a17701f2"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafb",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "695d83bd990a1117b3d0ce06cc888027d12a054c2677fd82f0d4fbfc93575523e7991a5e35a3752e9b70ce62992e268a877744cdd435f5f130869c9a2074b338"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfc",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "a6213743568e3b3158b9184301f3690847554c68457cb40fc9a4b8cfd8d4a118c301a07737aeda0f929c68913c5f51c80394f53bff1c3e83b2e40ca97eba9e15"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfd",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "d444bfa2362a96df213d070e33fa841f51334e4e76866b8139e8af3bb3398be2dfaddcbc56b9146de9f68118dc5829e74b0c28d7711907b121f9161cb92b69a9"
+    ),
+    (
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe",
+        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+        "142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461"
+    )
+  ]
diff --git a/test/Test/Crypto/Sodium/Key/Derivation.hs b/test/Test/Crypto/Sodium/Key/Derivation.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Crypto/Sodium/Key/Derivation.hs
@@ -0,0 +1,49 @@
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+-- | Tests for our cool slip-based KDF
+module Test.Crypto.Sodium.Key.Derivation where
+
+import Hedgehog (Gen, Property, (===), evalIO, forAll, property, tripping)
+
+import qualified Hedgehog.Gen as G
+import qualified Hedgehog.Range as R
+
+import Data.ByteString (ByteString)
+import Data.ByteArray.Sized (SizedByteArray)
+
+import qualified Libsodium as Na
+
+import qualified Test.Crypto.Sodium.Gen as G
+
+import Crypto.Sodium.Key (Params (Params), derive, rederive)
+
+import qualified Crypto.Sodium.Key.Internal as KI
+
+
+genParams :: Gen Params
+genParams = Params
+  <$> G.integral
+    (R.linear (fromIntegral Na.crypto_pwhash_opslimit_min) 10)
+  <*> G.integral
+    (R.linear (fromIntegral Na.crypto_pwhash_memlimit_min) (2 * 1024 * 1024))
+
+genSlipData :: Gen KI.DerivationSlipData
+genSlipData = KI.DerivationSlipData
+  <$> genParams
+  <*> G.nonce
+
+
+hprop_slip_encode_decode :: Property
+hprop_slip_encode_decode = property $ do
+    slipData <- forAll $ genSlipData
+    tripping slipData KI.derivationSlipEncode KI.derivationSlipDecode
+
+hprop_derive_rederive :: Property
+hprop_derive_rederive = property $ do
+    params <- forAll $ genParams
+    passwd <- forAll $ G.bytes (R.linear 0 100)
+    Just (key, slip) <- evalIO $
+      derive @(SizedByteArray 64 ByteString) params passwd
+    rederive slip passwd === Just key
diff --git a/test/Test/Crypto/Sodium/Nonce.hs b/test/Test/Crypto/Sodium/Nonce.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Crypto/Sodium/Nonce.hs
@@ -0,0 +1,69 @@
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+module Test.Crypto.Sodium.Nonce where
+
+import Test.HUnit ((@?), (@?=), Assertion)
+
+import Control.DeepSeq (deepseq)
+import Data.ByteArray.Sized (unSizedByteArray)
+import Data.ByteString (ByteString)
+import Data.Ratio ((%))
+import System.CPUTime (getCPUTime)
+
+import qualified Data.ByteString as BS
+import qualified Libsodium as Na
+
+import Crypto.Sodium.Nonce (generate)
+
+import qualified Crypto.Sodium.Encrypt.Symmetric as Symmetric
+import qualified Crypto.Sodium.Nonce as Nonce (generate)
+import qualified Crypto.Sodium.Pwhash.Internal as Pwhash
+import qualified Crypto.Sodium.Random as Random (generate)
+
+
+-- Well, this is kinda stupid, because we merely generate one random sequence,
+-- but this is just to check that the lengths are correctly propagated
+-- through types. So, good enough.
+
+unit_generate_Symmetric_nonce :: Assertion
+unit_generate_Symmetric_nonce = do
+  nonce <- generate :: IO (Symmetric.Nonce ByteString)
+  let bs = unSizedByteArray nonce
+  BS.length bs @?= fromIntegral Na.crypto_secretbox_noncebytes
+
+unit_generate_Pwhash_salt :: Assertion
+unit_generate_Pwhash_salt = do
+  nonce <- generate :: IO (Pwhash.Salt ByteString)
+  let bs = unSizedByteArray nonce
+  BS.length bs @?= fromIntegral Na.crypto_pwhash_saltbytes
+
+
+
+-- Benchmark to make sure this all makes sense and insecure nonse generation
+-- is actually faster than cryptographically-secure generation.
+unit_bench_against_crypto :: Assertion
+unit_bench_against_crypto = do
+    tNonce <- measure $ (unSizedByteArray <$> Nonce.generate @64)
+    tCrypto <- measure $ (unSizedByteArray <$> Random.generate @ByteString @64)
+    let ratio = fromRational (tNonce % tCrypto) :: Double
+
+    -- XXX: The benchmark is disabled, because we don’t yet have an
+    -- implementation that would actually be faster :/.
+    --        vvvvvvvv
+    ratio < 1 || True @? "Crypto gen is " <> show ratio <> "x faster"
+    --        ^^^^^^^^
+  where
+    measure act = do
+        t1 <- getCPUTime
+        go 1000
+        t2 <- getCPUTime
+        pure $ t2 - t1
+      where
+        go :: Int -> IO ()
+        go n
+          | n <= 0 = pure ()
+          | otherwise = do
+              res <- act
+              res `deepseq` go (n - 1)
diff --git a/test/Test/Crypto/Sodium/Pwhash.hs b/test/Test/Crypto/Sodium/Pwhash.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Crypto/Sodium/Pwhash.hs
@@ -0,0 +1,278 @@
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+
+module Test.Crypto.Sodium.Pwhash where
+
+import Test.HUnit ((@?=), Assertion)
+
+import Data.ByteArray.Sized (sizedByteArray)
+import Data.ByteString (ByteString)
+import Data.ByteString.Base16 (decodeBase16)
+import Data.Either (fromRight)
+import GHC.TypeLits (type (<=), KnownNat)
+
+import qualified Data.ByteString as BS
+import qualified Libsodium as Na
+
+import Crypto.Sodium.Pwhash.Internal (Algorithm (..), Params (Params), pwhash)
+
+
+pwhash_test_vector
+  ::  forall n. -- ^ Output length.
+      ( KnownNat n
+      , Na.CRYPTO_PWHASH_BYTES_MIN <= n, n <= Na.CRYPTO_PWHASH_BYTES_MAX
+      )
+  => ByteString  -- ^ Expected hash.
+  -> ByteString  -- ^ Password.
+  -> ByteString  -- ^ Salt.
+  -> Algorithm  -- ^ Hashing algorithm.
+  -> Params  -- ^ Hashing params.
+  -> Assertion
+pwhash_test_vector hash passwd salt alg params = do
+  let hash' = fromRight (error "impossible") . decodeBase16 $ hash
+  let passwd' = fromRight (error "impossible") . decodeBase16 $ passwd
+  let salt' = fromRight (error "impossible") . decodeBase16 $ salt
+  let Just salt'N = sizedByteArray (BS.take 16 salt')  -- Note:
+    -- for some reason, the test vectors in the file are 32 bytes long,
+    -- while the pwhash function needs a 16-byte salt :/
+  let Just hash'N = sizedByteArray @n hash'
+  result <- pwhash alg params passwd' salt'N
+  result @?= Just hash'N
+
+
+-- Test vectors from
+-- https://github.com/jedisct1/libsodium/blob/f911b56650b680ecfc5d32b11b090849fc2b5f92/test/default/pwhash_argon2id.c
+
+unit_pwhash_argon2id_1 :: Assertion
+unit_pwhash_argon2id_1 =
+  pwhash_test_vector
+    @155
+    "18acec5d6507739f203d1f5d9f1d862f7c2cdac4f19d2bdff64487e60d969e3ced615337b9eec6ac4461c6ca07f0939741e57c24d0005c7ea171a0ee1e7348249d135b38f222e4dad7b9a033ed83f5ca27277393e316582033c74affe2566a2bea47f91f0fd9fe49ece7e1f79f3ad6e9b23e0277c8ecc4b313225748dd2a80f5679534a0700e246a79a49b3f74eb89ec6205fe1eeb941c73b1fcf1"
+    (mconcat $
+      [ "a347ae92bce9f80f6f595a4480fc9c2fe7e7d7148d371e9487d75f5c23008ffae0"
+      , "65577a928febd9b1973a5a95073acdbeb6a030cfc0d79caa2dc5cd011cef02c08d"
+      , "a232d76d52dfbca38ca8dcbd665b17d1665f7cf5fe59772ec909733b24de97d6f5"
+      , "8d220b20c60d7c07ec1fd93c52c31020300c6c1facd77937a597c7a6"
+      ]
+    )
+    "5541fbc995d5c197ba290346d2c559dedf405cf97e5f95482143202f9e74f5c2"
+    Argon2id_1_3
+    (Params 5 7256678)
+
+unit_pwhash_argon2id_2 :: Assertion
+unit_pwhash_argon2id_2 =
+  pwhash_test_vector
+    @250
+    "26bab5f101560e48c711da4f05e81f5a3802b7a93d5155b9cab153069cc42b8e9f910bfead747652a0708d70e4de0bada37218bd203a1201c36b42f9a269b675b1f30cfc36f35a3030e9c7f57dfba0d341a974c1886f708c3e8297efbfe411bb9d51375264bd7c70d57a8a56fc9de2c1c97c08776803ec2cd0140bba8e61dc0f4ad3d3d1a89b4b710af81bfe35a0eea193e18a6da0f5ec05542c9eefc4584458e1da715611ba09617384748bd43b9bf1f3a6df4ecd091d0875e08d6e2fd8a5c7ce08904b5160cd38167b76ec76ef2d310049055a564da23d4ebd2b87e421cc33c401e12d5cd8d936c9baf75ebdfb557d342d2858fc781da31860"
+    (mconcat $
+      [ "e125cee61c8cb7778d9e5ad0a6f5d978ce9f84de213a8556d9ffe202020ab4a6ed"
+      , "9074a4eb3416f9b168f137510f3a30b70b96cbfa219ff99f6c6eaffb15c06b60e0"
+      , "0cc2890277f0fd3c622115772f7048adaebed86e"
+      ]
+    )
+    "f1192dd5dc2368b9cd421338b22433455ee0a3699f9379a08b9650ea2c126f0d"
+    Argon2id_1_3
+    (Params 4 7849083)
+
+unit_pwhash_argon2id_3 :: Assertion
+unit_pwhash_argon2id_3 =
+  pwhash_test_vector
+    @249
+    "6eb45e668582d63788ca8f6e930ca60b045a795fca987344f9a7a135aa3b5132b50a34a3864c26581f1f56dd0bcbfafbfa92cd9bff6b24a734cfe88f854aef4bda0a7983120f44936e8ff31d29728ac08ccce6f3f916b3c63962755c23a1fa9bb4e8823fc867bfd18f28980d94bc5874423ab7f96cc0ab78d8fa21fbd00cd3a1d96a73fa439ccc3fc4eab1590677b06cc78b0f674dfb680f23022fb902022dd8620803229c6ddf79a8156ccfce48bbd76c05ab670634f206e5b2e896230baa74a856964dbd8511acb71d75a1506766a125d8ce037f1db72086ebc3bccaefbd8cd9380167c2530386544ebfbeadbe237784d102bb92a10fd242"
+    (mconcat $
+      [ "92263cbf6ac376499f68a4289d3bb59e5a22335eba63a32e6410249155b956b6a3"
+      , "b48d4a44906b18b897127300b375b8f834f1ceffc70880a885f47c33876717e392"
+      , "be57f7da3ae58da4fd1f43daa7e44bb82d3717af4319349c24cd31e46d295856b0"
+      , "441b6b289992a11ced1cc3bf3011604590244a3eb737ff221129215e4e4347f491"
+      , "5d41292b5173d196eb9add693be5319fdadc242906178bb6c0286c9b6ca6012746"
+      , "711f58c8c392016b2fdfc09c64f0f6b6ab7b"
+      ]
+    )
+    "3b840e20e9555e9fb031c4ba1f1747ce25cc1d0ff664be676b9b4a90641ff194"
+    Argon2id_1_3
+    (Params 3 7994791)
+
+-- unit_pwhash_argon2id_4 is skipped because it is a test for an incorrect output
+-- size, which is impossible due to stronger types in this library
+
+unit_pwhash_argon2id_5 :: Assertion
+unit_pwhash_argon2id_5 =
+  pwhash_test_vector
+    @190
+    "08d8cd330c57e1b4643241d05bb468ba4ee4e932cd0858816be9ef15360b27bbd06a87130ee92222be267a29b81f5ae8fe8613324cfc4832dc49387fd0602f1c57b4d0f3855db94fb7e12eb05f9a484aed4a4307abf586cd3d55c809bc081541e00b682772fb2066504ff935b8ebc551a2083882f874bc0fae68e56848ae34c91097c3bf0cca8e75c0797eef3efde3f75e005815018db3cf7c109a812264c4de69dcb22322dbbcfa447f5b00ecd1b04a7be1569c8e556adb7bba48adf81d"
+    (mconcat $
+      [ "4a857e2ee8aa9b6056f2424e84d24a72473378906ee04a46cb05311502d5250b82"
+      , "ad86b83c8f20a23dbb74f6da60b0b6ecffd67134d45946ac8ebfb3064294bc097d"
+      , "43ced68642bfb8bbbdd0f50b30118f5e"
+      ]
+    )
+    "39d82eef32010b8b79cc5ba88ed539fbaba741100f2edbeca7cc171ffeabf258"
+    Argon2id_1_3
+    (Params 3 1432947)
+
+unit_pwhash_argon2id_6 :: Assertion
+unit_pwhash_argon2id_6 =
+  pwhash_test_vector
+    @178
+    "d6e9d6cabd42fb9ba7162fe9b8e41d59d3c7034756cb460c9affe393308bd0225ce0371f2e6c3ca32aca2002bf2d3909c6b6e7dfc4a00e850ff4f570f8f749d4bb6f0091e554be67a9095ae1eefaa1a933316cbec3c2fd4a14a5b6941bda9b7eabd821d79abde2475a53af1a8571c7ee46460be415882e0b393f48c12f740a6a72cba9773000602e13b40d3dfa6ac1d4ec43a838b7e3e165fecad4b2498389e60a3ff9f0f8f4b9fca1126e64f49501e38690"
+    (mconcat $
+      [ "c7b09aec680e7b42fedd7fc792e78b2f6c1bea8f4a884320b648f81e8cf515e8ba"
+      , "9dcfb11d43c4aae114c1734aa69ca82d44998365db9c93744fa28b63fd16000e82"
+      , "61cbbe083e7e2da1e5f696bde0834fe53146d7e0e35e7de9920d041f5a5621aabe"
+      , "02da3e2b09b405b77937efef3197bd5772e41fdb73fb5294478e45208063b5f58e"
+      , "089dbeb6d6342a909c1307b3fff5fe2cf4da56bdae50848f"
+      ]
+    )
+    "039c056d933b475032777edbaffac50f143f64c123329ed9cf59e3b65d3f43b6"
+    Argon2id_1_3
+    (Params 3 4886999)
+
+unit_pwhash_argon2id_7 :: Assertion
+unit_pwhash_argon2id_7 =
+  pwhash_test_vector
+    @231
+    "7fb72409b0987f8190c3729710e98c3f80c5a8727d425fdcde7f3644d467fe973f5b5fee683bd3fce812cb9ae5e9921a2d06c2f1905e4e839692f2b934b682f11a2fe2b90482ea5dd234863516dba6f52dc0702d324ec77d860c2e181f84472bd7104fedce071ffa93c5309494ad51623d214447a7b2b1462dc7d5d55a1f6fd5b54ce024118d86f0c6489d16545aaa87b6689dad9f2fb47fda9894f8e12b87d978b483ccd4cc5fd9595cdc7a818452f915ce2f7df95ec12b1c72e3788d473441d884f9748eb14703c21b45d82fd667b85f5b2d98c13303b3fe76285531a826b6fc0fe8e3dddecf"
+    (mconcat $
+      [ "b540beb016a5366524d4605156493f9874514a5aa58818cd0c6dfffaa9e90205f1"
+      , "7b"
+      ]
+    )
+    "44071f6d181561670bda728d43fb79b443bb805afdebaf98622b5165e01b15fb"
+    Argon2id_1_3
+    (Params 1 1631659)
+
+unit_pwhash_argon2id_8 :: Assertion
+unit_pwhash_argon2id_8 =
+  pwhash_test_vector
+    @167
+    "4e702bc5f891df884c6ddaa243aa846ce3c087fe930fef0f36b3c2be34164ccc295db509254743f18f947159c813bcd5dd8d94a3aec93bbe57605d1fad1aef1112687c3d4ef1cb329d21f1632f626818d766915d886e8d819e4b0b9c9307f4b6afc081e13b0cf31db382ff1bf05a16aac7af696336d75e99f82163e0f371e1d25c4add808e215697ad3f779a51a462f8bf52610af21fc69dba6b072606f2dabca7d4ae1d91d919"
+    (mconcat $
+      [ "a14975c26c088755a8b715ff2528d647cd343987fcf4aa25e7194a8417fb2b4b3f"
+      , "7268da9f3182b4cfb22d138b2749d673a47ecc7525dd15a0a3c66046971784bb63"
+      , "d7eae24cc84f2631712075a10e10a96b0e0ee67c43e01c423cb9c44e5371017e9c"
+      , "496956b632158da3fe12addecb88912e6759bc37f9af2f45af72c5cae3b179ffb6"
+      , "76a697de6ebe45cd4c16d4a9d642d29ddc0186a0a48cb6cd62bfc3dd229d313b30"
+      , "1560971e740e2cf1f99a9a090a5b283f35475057e96d7064e2e0fc81984591068d"
+      , "55a3b4169f22cccb0745a2689407ea1901a0a766eb99"
+      ]
+    )
+    "3d968b2752b8838431165059319f3ff8910b7b8ecb54ea01d3f54769e9d98daf"
+    Argon2id_1_3
+    (Params 3 1784128)
+
+
+-- Test vectors from
+-- https://github.com/jedisct1/libsodium/blob/f911b56650b680ecfc5d32b11b090849fc2b5f92/test/default/pwhash_argon2i.c
+
+unit_pwhash_argon2i_1 :: Assertion
+unit_pwhash_argon2i_1 =
+  pwhash_test_vector
+    @155
+    "23b803c84eaa25f4b44634cc1e5e37792c53fcd9b1eb20f865329c68e09cbfa9f1968757901b383fce221afe27713f97914a041395bbe1fb70e079e5bed2c7145b1f6154046f5958e9b1b29055454e264d1f2231c316f26be2e3738e83a80315e9a0951ce4b137b52e7d5ee7b37f7d936dcee51362bcf792595e3c896ad5042734fc90c92cae572ce63ff659a2f7974a3bd730d04d525d253ccc38"
+    (mconcat $
+      [ "a347ae92bce9f80f6f595a4480fc9c2fe7e7d7148d371e9487d75f5c23008ffae0"
+      , "65577a928febd9b1973a5a95073acdbeb6a030cfc0d79caa2dc5cd011cef02c08d"
+      , "a232d76d52dfbca38ca8dcbd665b17d1665f7cf5fe59772ec909733b24de97d6f5"
+      , "8d220b20c60d7c07ec1fd93c52c31020300c6c1facd77937a597c7a6"
+      ]
+    )
+    "5541fbc995d5c197ba290346d2c559dedf405cf97e5f95482143202f9e74f5c2"
+    Argon2i_1_3
+    (Params 5 7256678)
+
+unit_pwhash_argon2i_2 :: Assertion
+unit_pwhash_argon2i_2 =
+  pwhash_test_vector
+    @250
+    "0bb3769b064b9c43a9460476ab38c4a9a2470d55d4c992c6e723af895e4c07c09af41f22f90eab583a0c362d177f4677f212482fd145bfb9ac6211635e48461122bb49097b5fb0739d2cd22a39bf03d268e7495d4fd8d710aa156202f0a06e932ff513e6e7c76a4e98b6df5cf922f124791b1076ad904e6897271f5d7d24c5929e2a3b836d0f2f2697c2d758ee79bf1264f3fae65f3744e0f6d7d07ef6e8b35b70c0f88e9036325bfb24ac7f550351486da87aef10d6b0cb77d1cf6e31cf98399c6f241c605c6530dffb4764784f6c0b0bf601d4e4431e8b18dabdc3079c6e264302ade79f61cbd5497c95486340bb891a737223100be0429650"
+    (mconcat $
+      [ "e125cee61c8cb7778d9e5ad0a6f5d978ce9f84de213a8556d9ffe202020ab4a6ed"
+      , "9074a4eb3416f9b168f137510f3a30b70b96cbfa219ff99f6c6eaffb15c06b60e0"
+      , "0cc2890277f0fd3c622115772f7048adaebed86e"
+      ]
+    )
+    "f1192dd5dc2368b9cd421338b22433455ee0a3699f9379a08b9650ea2c126f0d"
+    Argon2i_1_3
+    (Params 4 7849083)
+
+unit_pwhash_argon2i_3 :: Assertion
+unit_pwhash_argon2i_3 =
+  pwhash_test_vector
+    @249
+    "e9aa073b0b872f15c083d1d7ce52c09f493b827ca78f13a06c1721b45b1e17b24c04e19fe869333135360197a7eb55994fee3e8d9680aedfdf7674f3ad7b84d59d7eab03579ffc10c7093093bc48ec84252aa1b30f40f5e838f1443e15e2772a39f4e774eb052097e8881e94f15457b779fa2af2bbc9a993687657c7704ac8a37c25c1df4289eb4c70da45f2fd46bc0f78259767d3dd478a7c369cf866758bc36d9bd8e2e3c9fb0cf7fd6073ebf630c1f67fa7d303c07da40b36749d157ea37965fef810f2ea05ae6fc7d96a8f3470d73e15b22b42e8d6986dbfe5303256b2b3560372c4452ffb2a04fb7c6691489f70cb46831be0679117f7"
+    (mconcat $
+      [ "92263cbf6ac376499f68a4289d3bb59e5a22335eba63a32e6410249155b956b6a3"
+      , "b48d4a44906b18b897127300b375b8f834f1ceffc70880a885f47c33876717e392"
+      , "be57f7da3ae58da4fd1f43daa7e44bb82d3717af4319349c24cd31e46d295856b0"
+      , "441b6b289992a11ced1cc3bf3011604590244a3eb737ff221129215e4e4347f491"
+      , "5d41292b5173d196eb9add693be5319fdadc242906178bb6c0286c9b6ca6012746"
+      , "711f58c8c392016b2fdfc09c64f0f6b6ab7b"
+      ]
+    )
+    "3b840e20e9555e9fb031c4ba1f1747ce25cc1d0ff664be676b9b4a90641ff194"
+    Argon2i_1_3
+    (Params 3 7994791)
+
+-- unit_pwhash_argon2i_4 is skipped because it is a test for an incorrect output
+-- size, which is impossible due to stronger types in this library
+
+unit_pwhash_argon2i_5 :: Assertion
+unit_pwhash_argon2i_5 =
+  pwhash_test_vector
+    @190
+    "c121209f0ba70aed93d49200e5dc82cce013cef25ea31e160bf8db3cf448a59d1a56f6c19259e18ea020553cb75781761d112b2d949a297584c65e60df95ad89c4109825a3171dc6f20b1fd6b0cdfd194861bc2b414295bee5c6c52619e544abce7d520659c3d51de2c60e89948d830695ab38dcb75dd7ab06a4770dd4bc7c8f335519e04b038416b1a7dbd25c026786a8105c5ffe7a0931364f0376ae5772be39b51d91d3281464e0f3a128e7155a68e87cf79626ffca0b2a3022fc8420"
+    (mconcat $
+      [ "4a857e2ee8aa9b6056f2424e84d24a72473378906ee04a46cb05311502d5250b82"
+      , "ad86b83c8f20a23dbb74f6da60b0b6ecffd67134d45946ac8ebfb3064294bc097d"
+      , "43ced68642bfb8bbbdd0f50b30118f5e"
+      ]
+    )
+    "39d82eef32010b8b79cc5ba88ed539fbaba741100f2edbeca7cc171ffeabf258"
+    Argon2i_1_3
+    (Params 3 1432947)
+
+unit_pwhash_argon2i_6 :: Assertion
+unit_pwhash_argon2i_6 =
+  pwhash_test_vector
+    @178
+    "91c337ce8918a5805a59b00bd1819d3eb4356807cbd2a80b271c4b482dce03f5b02ae4eb831ff668cbb327b93c300b41da4852e5547bea8342d518dd9311aaeb5f90eccf66d548f9275631f0b1fd4b299cec5d2e86a59e55dc7b3afab6204447b21d1ef1da824abaf31a25a0d6135c4fe81d34a06816c8a6eab19141f5687108500f3719a862af8c5fee36e130c69921e11ce83dfc72c5ec3b862c1bccc5fd63ad57f432fbcca6f9e18d5a59015950cdf053"
+    (mconcat $
+      [ "c7b09aec680e7b42fedd7fc792e78b2f6c1bea8f4a884320b648f81e8cf515e8ba"
+      , "9dcfb11d43c4aae114c1734aa69ca82d44998365db9c93744fa28b63fd16000e82"
+      , "61cbbe083e7e2da1e5f696bde0834fe53146d7e0e35e7de9920d041f5a5621aabe"
+      , "02da3e2b09b405b77937efef3197bd5772e41fdb73fb5294478e45208063b5f58e"
+      , "089dbeb6d6342a909c1307b3fff5fe2cf4da56bdae50848f"
+      ]
+    )
+    "039c056d933b475032777edbaffac50f143f64c123329ed9cf59e3b65d3f43b6"
+    Argon2i_1_3
+    (Params 3 4886999)
+
+-- unit_pwhash_argon2i_7 is skipped because it is a test for an incorrect
+-- opslimit.
+-- XXX: Maybe we could encode this restriction in types?
+
+unit_pwhash_argon2i_8 :: Assertion
+unit_pwhash_argon2i_8 =
+  pwhash_test_vector
+    @167
+    "e942951dfbc2d508294b10f9e97b47d0cd04e668a043cb95679cc1139df7c27cd54367688725be9d069f5704c12223e7e4ca181fbd0bed18bb4634795e545a6c04a7306933a41a794baedbb628d41bc285e0b9084055ae136f6b63624c874f5a1e1d8be7b0b7227a171d2d7ed578d88bfdcf18323198962d0dcad4126fd3f21adeb1e11d66252ea0c58c91696e91031bfdcc2a9dc0e028d17b9705ba2d7bcdcd1e3ba75b4b1fea"
+    (mconcat $
+      [ "a14975c26c088755a8b715ff2528d647cd343987fcf4aa25e7194a8417fb2b4b3f"
+      , "7268da9f3182b4cfb22d138b2749d673a47ecc7525dd15a0a3c66046971784bb63"
+      , "d7eae24cc84f2631712075a10e10a96b0e0ee67c43e01c423cb9c44e5371017e9c"
+      , "496956b632158da3fe12addecb88912e6759bc37f9af2f45af72c5cae3b179ffb6"
+      , "76a697de6ebe45cd4c16d4a9d642d29ddc0186a0a48cb6cd62bfc3dd229d313b30"
+      , "1560971e740e2cf1f99a9a090a5b283f35475057e96d7064e2e0fc81984591068d"
+      , "55a3b4169f22cccb0745a2689407ea1901a0a766eb99"
+      ]
+    )
+    "3d968b2752b8838431165059319f3ff8910b7b8ecb54ea01d3f54769e9d98daf"
+    Argon2i_1_3
+    (Params 3 1784128)
diff --git a/test/Test/Crypto/Sodium/Random.hs b/test/Test/Crypto/Sodium/Random.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Crypto/Sodium/Random.hs
@@ -0,0 +1,36 @@
+-- SPDX-FileCopyrightText: 2020 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+module Test.Crypto.Sodium.Random where
+
+import Test.HUnit ((@?=), Assertion)
+
+import Data.ByteArray.Sized (unSizedByteArray)
+import Data.ByteString (ByteString)
+
+import qualified Data.ByteString as BS
+import qualified Libsodium as Na
+
+import Crypto.Sodium.Random (generate)
+
+import qualified Crypto.Sodium.Encrypt.Symmetric as Symmetric
+
+
+-- Well, this is kinda stupid, because we merely generate one random sequence,
+-- but this is just to check that the lengths are correctly propagated
+-- through types. So, good enough.
+--
+-- Also it is not thread-safe, since we don’t call @sodiumInit@...
+
+unit_generate_Symmetric_key :: Assertion
+unit_generate_Symmetric_key = do
+  key <- generate :: IO (Symmetric.Key ByteString)
+  let bs = unSizedByteArray key
+  BS.length bs @?= fromIntegral Na.crypto_secretbox_keybytes
+
+unit_generate_Symmetric_nonce :: Assertion
+unit_generate_Symmetric_nonce = do
+  nonce <- generate :: IO (Symmetric.Nonce ByteString)
+  let bs = unSizedByteArray nonce
+  BS.length bs @?= fromIntegral Na.crypto_secretbox_noncebytes
diff --git a/test/Test/Crypto/Sodium/Sign.hs b/test/Test/Crypto/Sodium/Sign.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Crypto/Sodium/Sign.hs
@@ -0,0 +1,36 @@
+-- SPDX-FileCopyrightText: 2021 Serokell
+--
+-- SPDX-License-Identifier: MPL-2.0
+
+-- | “Integration” tests: using Sign with our helpers.
+module Test.Crypto.Sodium.Sign where
+
+import Hedgehog (Property, evalMaybe, forAll, property, tripping)
+import Hedgehog.Internal.Property (forAllT)
+
+import Control.Monad.IO.Class (liftIO)
+import Data.ByteArray.Sized (sizedByteArray)
+import Data.ByteString (ByteString)
+
+import qualified Hedgehog.Gen as G
+import qualified Hedgehog.Range as R
+
+import qualified Libsodium as Na
+
+import qualified Crypto.Sodium.Sign as Sign
+
+
+seedSize :: R.Range Int
+seedSize = R.singleton $ fromIntegral Na.crypto_sign_seedbytes
+
+
+hprop_encode_decode_seed :: Property
+hprop_encode_decode_seed = property $ do
+    seed <- evalMaybe . sizedByteArray =<< forAll (G.bytes seedSize)
+    (pk, sk) <- forAllT . liftIO $ Sign.keypairFromSeed seed
+    msg <- forAll $ G.bytes (R.linear 0 1_000)
+    tripping msg (encodeBs sk) (decodeBs pk)
+  where
+    -- We need to specify the type of the signed msg as it is polymorphic
+    encodeBs sk msg = Sign.create sk msg :: ByteString
+    decodeBs pk ct = Sign.open pk ct :: Maybe ByteString
