sel 0.0.2.0 → 0.1.0.0
raw patch · 33 files changed
+962/−656 lines, 33 filesdep +text-builder-lineardep +transformersdep ~libsodium-bindingsdep ~tastydep ~text-displayPVP ok
version bump matches the API change (PVP)
Dependencies added: text-builder-linear, transformers
Dependency ranges changed: libsodium-bindings, tasty, text-display
API changes (from Hackage documentation)
- Sel.HMAC.SHA256: instance GHC.Show.Show Sel.HMAC.SHA256.AuthenticationKey
- Sel.HMAC.SHA256: instance GHC.Show.Show Sel.HMAC.SHA256.AuthenticationTag
- Sel.HMAC.SHA512: instance GHC.Show.Show Sel.HMAC.SHA512.AuthenticationKey
- Sel.HMAC.SHA512: instance GHC.Show.Show Sel.HMAC.SHA512.AuthenticationTag
- Sel.HMAC.SHA512_256: instance GHC.Show.Show Sel.HMAC.SHA512_256.AuthenticationKey
- Sel.HMAC.SHA512_256: instance GHC.Show.Show Sel.HMAC.SHA512_256.AuthenticationTag
- Sel.Hashing: instance Foreign.Storable.Storable Sel.Hashing.Hash
- Sel.Hashing: instance GHC.Show.Show Sel.Hashing.Hash
- Sel.Hashing.Password: instance GHC.Generics.Generic Sel.Hashing.Password.PasswordHash
- Sel.Hashing.Password: instance GHC.Show.Show Sel.Hashing.Password.PasswordHash
- Sel.Hashing.Password: instance GHC.Show.Show Sel.Hashing.Password.Salt
- Sel.Hashing.SHA256: instance Foreign.Storable.Storable Sel.Hashing.SHA256.Hash
- Sel.Hashing.SHA256: instance GHC.Show.Show Sel.Hashing.SHA256.Hash
- Sel.Hashing.SHA512: instance Foreign.Storable.Storable Sel.Hashing.SHA512.Hash
- Sel.Hashing.SHA512: instance GHC.Show.Show Sel.Hashing.SHA512.Hash
- Sel.Hashing.Short: instance GHC.Exception.Type.Exception Sel.Hashing.Short.ShortHashingException
- Sel.Hashing.Short: instance GHC.Show.Show Sel.Hashing.Short.ShortHash
- Sel.Hashing.Short: instance GHC.Show.Show Sel.Hashing.Short.ShortHashKey
- Sel.Hashing.Short: instance GHC.Show.Show Sel.Hashing.Short.ShortHashingException
- Sel.PublicKey.Cipher: CipherText :: CULLong -> ForeignPtr CUChar -> CipherText
- Sel.PublicKey.Cipher: [cipherTextForeignPtr] :: CipherText -> ForeignPtr CUChar
- Sel.PublicKey.Cipher: cipherTextFromHexByteString :: StrictByteString -> Either Text CipherText
- Sel.PublicKey.Cipher: cipherTextToBinary :: CipherText -> StrictByteString
- Sel.PublicKey.Cipher: cipherTextToHexByteString :: CipherText -> StrictByteString
- Sel.PublicKey.Cipher: cipherTextToHexText :: CipherText -> Text
- Sel.PublicKey.Cipher: data CipherText
- Sel.PublicKey.Cipher: instance Data.Text.Display.Core.Display Sel.PublicKey.Cipher.CipherText
- Sel.PublicKey.Cipher: instance GHC.Classes.Eq Sel.PublicKey.Cipher.CipherText
- Sel.PublicKey.Cipher: instance GHC.Classes.Ord Sel.PublicKey.Cipher.CipherText
- Sel.PublicKey.Cipher: instance GHC.Exception.Type.Exception Sel.PublicKey.Cipher.EncryptionError
- Sel.PublicKey.Cipher: instance GHC.Exception.Type.Exception Sel.PublicKey.Cipher.KeyPairGenerationException
- Sel.PublicKey.Cipher: instance GHC.Show.Show Sel.PublicKey.Cipher.CipherText
- Sel.PublicKey.Cipher: instance GHC.Show.Show Sel.PublicKey.Cipher.EncryptionError
- Sel.PublicKey.Cipher: instance GHC.Show.Show Sel.PublicKey.Cipher.KeyPairGenerationException
- Sel.PublicKey.Cipher: instance GHC.Show.Show Sel.PublicKey.Cipher.Nonce
- Sel.PublicKey.Cipher: instance GHC.Show.Show Sel.PublicKey.Cipher.PublicKey
- Sel.PublicKey.Cipher: instance GHC.Show.Show Sel.PublicKey.Cipher.SecretKey
- Sel.Scrypt: instance GHC.Show.Show Sel.Scrypt.ScryptHash
- Sel.SecretKey.Authentication: instance GHC.Show.Show Sel.SecretKey.Authentication.AuthenticationKey
- Sel.SecretKey.Authentication: instance GHC.Show.Show Sel.SecretKey.Authentication.AuthenticationTag
- Sel.SecretKey.Cipher: data Hash
- Sel.SecretKey.Cipher: hashFromHexByteString :: StrictByteString -> Either Text Hash
- Sel.SecretKey.Cipher: hashToBinary :: Hash -> StrictByteString
- Sel.SecretKey.Cipher: hashToHexByteString :: Hash -> StrictByteString
- Sel.SecretKey.Cipher: hashToHexText :: Hash -> Text
- Sel.SecretKey.Cipher: instance Data.Text.Display.Core.Display Sel.SecretKey.Cipher.Hash
- Sel.SecretKey.Cipher: instance GHC.Classes.Eq Sel.SecretKey.Cipher.Hash
- Sel.SecretKey.Cipher: instance GHC.Classes.Ord Sel.SecretKey.Cipher.Hash
- Sel.SecretKey.Cipher: instance GHC.Show.Show Sel.SecretKey.Cipher.Hash
- Sel.SecretKey.Cipher: instance GHC.Show.Show Sel.SecretKey.Cipher.Nonce
- Sel.SecretKey.Cipher: instance GHC.Show.Show Sel.SecretKey.Cipher.SecretKey
- Sel.SecretKey.Stream: data CipherText
- Sel.SecretKey.Stream: instance Data.Text.Display.Core.Display Sel.SecretKey.Stream.CipherText
- Sel.SecretKey.Stream: instance GHC.Classes.Eq Sel.SecretKey.Stream.CipherText
- Sel.SecretKey.Stream: instance GHC.Classes.Ord Sel.SecretKey.Stream.CipherText
- Sel.SecretKey.Stream: instance GHC.Exception.Type.Exception Sel.SecretKey.Stream.StreamDecryptionException
- Sel.SecretKey.Stream: instance GHC.Exception.Type.Exception Sel.SecretKey.Stream.StreamEncryptionException
- Sel.SecretKey.Stream: instance GHC.Exception.Type.Exception Sel.SecretKey.Stream.StreamInitEncryptionException
- Sel.SecretKey.Stream: instance GHC.Show.Show Sel.SecretKey.Stream.CipherText
- Sel.SecretKey.Stream: instance GHC.Show.Show Sel.SecretKey.Stream.Header
- Sel.SecretKey.Stream: instance GHC.Show.Show Sel.SecretKey.Stream.SecretKey
- Sel.SecretKey.Stream: instance GHC.Show.Show Sel.SecretKey.Stream.StreamDecryptionException
- Sel.SecretKey.Stream: instance GHC.Show.Show Sel.SecretKey.Stream.StreamEncryptionException
- Sel.SecretKey.Stream: instance GHC.Show.Show Sel.SecretKey.Stream.StreamInitEncryptionException
+ Sel.HMAC.SHA256: instance GHC.Internal.Show.Show Sel.HMAC.SHA256.AuthenticationKey
+ Sel.HMAC.SHA256: instance GHC.Internal.Show.Show Sel.HMAC.SHA256.AuthenticationTag
+ Sel.HMAC.SHA512: instance GHC.Internal.Show.Show Sel.HMAC.SHA512.AuthenticationKey
+ Sel.HMAC.SHA512: instance GHC.Internal.Show.Show Sel.HMAC.SHA512.AuthenticationTag
+ Sel.HMAC.SHA512_256: instance GHC.Internal.Show.Show Sel.HMAC.SHA512_256.AuthenticationKey
+ Sel.HMAC.SHA512_256: instance GHC.Internal.Show.Show Sel.HMAC.SHA512_256.AuthenticationTag
+ Sel.Hashing: instance GHC.Internal.Foreign.Storable.Storable Sel.Hashing.Hash
+ Sel.Hashing: instance GHC.Internal.Show.Show Sel.Hashing.Hash
+ Sel.Hashing.Password: instance GHC.Internal.Generics.Generic Sel.Hashing.Password.PasswordHash
+ Sel.Hashing.Password: instance GHC.Internal.Show.Show Sel.Hashing.Password.PasswordHash
+ Sel.Hashing.Password: instance GHC.Internal.Show.Show Sel.Hashing.Password.Salt
+ Sel.Hashing.SHA256: instance GHC.Internal.Foreign.Storable.Storable Sel.Hashing.SHA256.Hash
+ Sel.Hashing.SHA256: instance GHC.Internal.Show.Show Sel.Hashing.SHA256.Hash
+ Sel.Hashing.SHA512: instance GHC.Internal.Foreign.Storable.Storable Sel.Hashing.SHA512.Hash
+ Sel.Hashing.SHA512: instance GHC.Internal.Show.Show Sel.Hashing.SHA512.Hash
+ Sel.Hashing.Short: instance GHC.Internal.Exception.Type.Exception Sel.Hashing.Short.ShortHashingException
+ Sel.Hashing.Short: instance GHC.Internal.Show.Show Sel.Hashing.Short.ShortHash
+ Sel.Hashing.Short: instance GHC.Internal.Show.Show Sel.Hashing.Short.ShortHashKey
+ Sel.Hashing.Short: instance GHC.Internal.Show.Show Sel.Hashing.Short.ShortHashingException
+ Sel.PublicKey.Cipher: Ciphertext :: CULLong -> ForeignPtr CUChar -> Ciphertext
+ Sel.PublicKey.Cipher: [ciphertextForeignPtr] :: Ciphertext -> ForeignPtr CUChar
+ Sel.PublicKey.Cipher: ciphertextFromHexByteString :: StrictByteString -> Either Text Ciphertext
+ Sel.PublicKey.Cipher: ciphertextToBinary :: Ciphertext -> StrictByteString
+ Sel.PublicKey.Cipher: ciphertextToHexByteString :: Ciphertext -> StrictByteString
+ Sel.PublicKey.Cipher: ciphertextToHexText :: Ciphertext -> Text
+ Sel.PublicKey.Cipher: data Ciphertext
+ Sel.PublicKey.Cipher: instance Data.Text.Display.Core.Display Sel.PublicKey.Cipher.Ciphertext
+ Sel.PublicKey.Cipher: instance GHC.Classes.Eq Sel.PublicKey.Cipher.Ciphertext
+ Sel.PublicKey.Cipher: instance GHC.Classes.Ord Sel.PublicKey.Cipher.Ciphertext
+ Sel.PublicKey.Cipher: instance GHC.Internal.Exception.Type.Exception Sel.PublicKey.Cipher.EncryptionError
+ Sel.PublicKey.Cipher: instance GHC.Internal.Exception.Type.Exception Sel.PublicKey.Cipher.KeyPairGenerationException
+ Sel.PublicKey.Cipher: instance GHC.Internal.Show.Show Sel.PublicKey.Cipher.Ciphertext
+ Sel.PublicKey.Cipher: instance GHC.Internal.Show.Show Sel.PublicKey.Cipher.EncryptionError
+ Sel.PublicKey.Cipher: instance GHC.Internal.Show.Show Sel.PublicKey.Cipher.KeyPairGenerationException
+ Sel.PublicKey.Cipher: instance GHC.Internal.Show.Show Sel.PublicKey.Cipher.Nonce
+ Sel.PublicKey.Cipher: instance GHC.Internal.Show.Show Sel.PublicKey.Cipher.PublicKey
+ Sel.PublicKey.Cipher: instance GHC.Internal.Show.Show Sel.PublicKey.Cipher.SecretKey
+ Sel.Scrypt: instance GHC.Internal.Show.Show Sel.Scrypt.ScryptHash
+ Sel.SecretKey.Authentication: instance GHC.Internal.Show.Show Sel.SecretKey.Authentication.AuthenticationKey
+ Sel.SecretKey.Authentication: instance GHC.Internal.Show.Show Sel.SecretKey.Authentication.AuthenticationTag
+ Sel.SecretKey.Cipher: ciphertextFromHexByteString :: StrictByteString -> Either Text Ciphertext
+ Sel.SecretKey.Cipher: ciphertextToBinary :: Ciphertext -> StrictByteString
+ Sel.SecretKey.Cipher: ciphertextToHexByteString :: Ciphertext -> StrictByteString
+ Sel.SecretKey.Cipher: ciphertextToHexText :: Ciphertext -> Text
+ Sel.SecretKey.Cipher: data Ciphertext
+ Sel.SecretKey.Cipher: instance Data.Text.Display.Core.Display Sel.SecretKey.Cipher.Ciphertext
+ Sel.SecretKey.Cipher: instance GHC.Classes.Eq Sel.SecretKey.Cipher.Ciphertext
+ Sel.SecretKey.Cipher: instance GHC.Classes.Ord Sel.SecretKey.Cipher.Ciphertext
+ Sel.SecretKey.Cipher: instance GHC.Internal.Show.Show Sel.SecretKey.Cipher.Ciphertext
+ Sel.SecretKey.Cipher: instance GHC.Internal.Show.Show Sel.SecretKey.Cipher.Nonce
+ Sel.SecretKey.Cipher: instance GHC.Internal.Show.Show Sel.SecretKey.Cipher.SecretKey
+ Sel.SecretKey.Stream: AdditionalData :: StrictByteString -> AdditionalData
+ Sel.SecretKey.Stream: AdditionalDataHexDecodingError :: Text -> AdditionalDataHexDecodingError
+ Sel.SecretKey.Stream: additionalDataFromHexByteString :: Base16 StrictByteString -> Either AdditionalDataHexDecodingError AdditionalData
+ Sel.SecretKey.Stream: additionalDataToBinary :: AdditionalData -> StrictByteString
+ Sel.SecretKey.Stream: additionalDataToHexByteString :: AdditionalData -> Base16 StrictByteString
+ Sel.SecretKey.Stream: additionalDataToHexText :: AdditionalData -> Base16 Text
+ Sel.SecretKey.Stream: data Ciphertext
+ Sel.SecretKey.Stream: instance Data.Text.Display.Core.Display Sel.SecretKey.Stream.AdditionalData
+ Sel.SecretKey.Stream: instance Data.Text.Display.Core.Display Sel.SecretKey.Stream.Ciphertext
+ Sel.SecretKey.Stream: instance GHC.Classes.Eq Sel.SecretKey.Stream.AdditionalData
+ Sel.SecretKey.Stream: instance GHC.Classes.Eq Sel.SecretKey.Stream.AdditionalDataHexDecodingError
+ Sel.SecretKey.Stream: instance GHC.Classes.Eq Sel.SecretKey.Stream.Ciphertext
+ Sel.SecretKey.Stream: instance GHC.Classes.Ord Sel.SecretKey.Stream.Ciphertext
+ Sel.SecretKey.Stream: instance GHC.Internal.Exception.Type.Exception Sel.SecretKey.Stream.StreamDecryptionException
+ Sel.SecretKey.Stream: instance GHC.Internal.Exception.Type.Exception Sel.SecretKey.Stream.StreamEncryptionException
+ Sel.SecretKey.Stream: instance GHC.Internal.Exception.Type.Exception Sel.SecretKey.Stream.StreamInitEncryptionException
+ Sel.SecretKey.Stream: instance GHC.Internal.Show.Show Sel.SecretKey.Stream.AdditionalData
+ Sel.SecretKey.Stream: instance GHC.Internal.Show.Show Sel.SecretKey.Stream.AdditionalDataHexDecodingError
+ Sel.SecretKey.Stream: instance GHC.Internal.Show.Show Sel.SecretKey.Stream.Ciphertext
+ Sel.SecretKey.Stream: instance GHC.Internal.Show.Show Sel.SecretKey.Stream.Header
+ Sel.SecretKey.Stream: instance GHC.Internal.Show.Show Sel.SecretKey.Stream.SecretKey
+ Sel.SecretKey.Stream: instance GHC.Internal.Show.Show Sel.SecretKey.Stream.StreamDecryptionException
+ Sel.SecretKey.Stream: instance GHC.Internal.Show.Show Sel.SecretKey.Stream.StreamEncryptionException
+ Sel.SecretKey.Stream: instance GHC.Internal.Show.Show Sel.SecretKey.Stream.StreamInitEncryptionException
+ Sel.SecretKey.Stream: newtype AdditionalData
+ Sel.SecretKey.Stream: newtype AdditionalDataHexDecodingError
- Sel.HMAC.SHA256: authenticate :: StrictByteString -> AuthenticationKey -> IO AuthenticationTag
+ Sel.HMAC.SHA256: authenticate :: StrictByteString -> AuthenticationKey -> AuthenticationTag
- Sel.HMAC.SHA256: withMultipart :: forall (a :: Type) (m :: Type -> Type). MonadIO m => AuthenticationKey -> (forall s. Multipart s -> m a) -> m AuthenticationTag
+ Sel.HMAC.SHA256: withMultipart :: forall a m. MonadIO m => AuthenticationKey -> (forall s. () => Multipart s -> m a) -> m AuthenticationTag
- Sel.HMAC.SHA512: authenticate :: StrictByteString -> AuthenticationKey -> IO AuthenticationTag
+ Sel.HMAC.SHA512: authenticate :: StrictByteString -> AuthenticationKey -> AuthenticationTag
- Sel.HMAC.SHA512: withMultipart :: forall (a :: Type) (m :: Type -> Type). MonadIO m => AuthenticationKey -> (forall s. Multipart s -> m a) -> m AuthenticationTag
+ Sel.HMAC.SHA512: withMultipart :: forall a m. MonadIO m => AuthenticationKey -> (forall s. () => Multipart s -> m a) -> m AuthenticationTag
- Sel.HMAC.SHA512_256: authenticate :: StrictByteString -> AuthenticationKey -> IO AuthenticationTag
+ Sel.HMAC.SHA512_256: authenticate :: StrictByteString -> AuthenticationKey -> AuthenticationTag
- Sel.HMAC.SHA512_256: withMultipart :: forall (a :: Type) (m :: Type -> Type). MonadIO m => AuthenticationKey -> (forall s. Multipart s -> m a) -> m AuthenticationTag
+ Sel.HMAC.SHA512_256: withMultipart :: forall a m. MonadIO m => AuthenticationKey -> (forall s. () => Multipart s -> m a) -> m AuthenticationTag
- Sel.Hashing: hashByteString :: Maybe HashKey -> StrictByteString -> IO Hash
+ Sel.Hashing: hashByteString :: Maybe HashKey -> StrictByteString -> Hash
- Sel.Hashing: updateMultipart :: forall (m :: Type -> Type) (s :: Type). MonadIO m => Multipart s -> StrictByteString -> m ()
+ Sel.Hashing: updateMultipart :: MonadIO m => Multipart s -> StrictByteString -> m ()
- Sel.Hashing: withMultipart :: forall (a :: Type) (m :: Type -> Type). MonadIO m => Maybe HashKey -> (forall s. Multipart s -> m a) -> m Hash
+ Sel.Hashing: withMultipart :: forall a m. MonadIO m => Maybe HashKey -> (forall s. () => Multipart s -> m a) -> m Hash
- Sel.Hashing.Password: hashByteStringWithParams :: Argon2Params -> Salt -> StrictByteString -> IO PasswordHash
+ Sel.Hashing.Password: hashByteStringWithParams :: Argon2Params -> Salt -> StrictByteString -> PasswordHash
- Sel.Hashing.SHA256: hashByteString :: StrictByteString -> IO Hash
+ Sel.Hashing.SHA256: hashByteString :: StrictByteString -> Hash
- Sel.Hashing.SHA256: hashText :: Text -> IO Hash
+ Sel.Hashing.SHA256: hashText :: Text -> Hash
- Sel.Hashing.SHA256: withMultipart :: forall (a :: Type) (m :: Type -> Type). MonadIO m => (forall s. Multipart s -> m a) -> m Hash
+ Sel.Hashing.SHA256: withMultipart :: forall a m. MonadIO m => (forall s. () => Multipart s -> m a) -> m Hash
- Sel.Hashing.SHA512: hashByteString :: StrictByteString -> IO Hash
+ Sel.Hashing.SHA512: hashByteString :: StrictByteString -> Hash
- Sel.Hashing.SHA512: hashText :: Text -> IO Hash
+ Sel.Hashing.SHA512: hashText :: Text -> Hash
- Sel.Hashing.SHA512: withMultipart :: forall (a :: Type) (m :: Type -> Type). MonadIO m => (forall s. Multipart s -> m a) -> m Hash
+ Sel.Hashing.SHA512: withMultipart :: forall a m. MonadIO m => (forall s. () => Multipart s -> m a) -> m Hash
- Sel.Hashing.Short: hashByteString :: ShortHashKey -> StrictByteString -> IO ShortHash
+ Sel.Hashing.Short: hashByteString :: ShortHashKey -> StrictByteString -> ShortHash
- Sel.Hashing.Short: hashText :: ShortHashKey -> Text -> IO ShortHash
+ Sel.Hashing.Short: hashText :: ShortHashKey -> Text -> ShortHash
- Sel.PublicKey.Cipher: [messageLength] :: CipherText -> CULLong
+ Sel.PublicKey.Cipher: [messageLength] :: Ciphertext -> CULLong
- Sel.PublicKey.Cipher: decrypt :: CipherText -> PublicKey -> SecretKey -> Nonce -> Maybe StrictByteString
+ Sel.PublicKey.Cipher: decrypt :: Ciphertext -> PublicKey -> SecretKey -> Nonce -> Maybe StrictByteString
- Sel.PublicKey.Cipher: encrypt :: StrictByteString -> PublicKey -> SecretKey -> IO (Nonce, CipherText)
+ Sel.PublicKey.Cipher: encrypt :: StrictByteString -> PublicKey -> SecretKey -> IO (Nonce, Ciphertext)
- Sel.PublicKey.Seal: open :: CipherText -> PublicKey -> SecretKey -> Maybe StrictByteString
+ Sel.PublicKey.Seal: open :: Ciphertext -> PublicKey -> SecretKey -> Maybe StrictByteString
- Sel.PublicKey.Seal: seal :: StrictByteString -> PublicKey -> IO CipherText
+ Sel.PublicKey.Seal: seal :: StrictByteString -> PublicKey -> IO Ciphertext
- Sel.SecretKey.Authentication: authenticate :: StrictByteString -> AuthenticationKey -> IO AuthenticationTag
+ Sel.SecretKey.Authentication: authenticate :: StrictByteString -> AuthenticationKey -> AuthenticationTag
- Sel.SecretKey.Cipher: decrypt :: Hash -> SecretKey -> Nonce -> Maybe StrictByteString
+ Sel.SecretKey.Cipher: decrypt :: Ciphertext -> SecretKey -> Nonce -> Maybe StrictByteString
- Sel.SecretKey.Cipher: encrypt :: StrictByteString -> SecretKey -> IO (Nonce, Hash)
+ Sel.SecretKey.Cipher: encrypt :: StrictByteString -> SecretKey -> IO (Nonce, Ciphertext)
- Sel.SecretKey.Stream: ciphertextFromHexByteString :: Base16 StrictByteString -> Either Text CipherText
+ Sel.SecretKey.Stream: ciphertextFromHexByteString :: Base16 StrictByteString -> Either Text Ciphertext
- Sel.SecretKey.Stream: ciphertextToBinary :: CipherText -> StrictByteString
+ Sel.SecretKey.Stream: ciphertextToBinary :: Ciphertext -> StrictByteString
- Sel.SecretKey.Stream: ciphertextToHexByteString :: CipherText -> Base16 StrictByteString
+ Sel.SecretKey.Stream: ciphertextToHexByteString :: Ciphertext -> Base16 StrictByteString
- Sel.SecretKey.Stream: ciphertextToHexText :: CipherText -> Base16 Text
+ Sel.SecretKey.Stream: ciphertextToHexText :: Ciphertext -> Base16 Text
- Sel.SecretKey.Stream: decryptChunk :: forall m s. MonadIO m => Multipart s -> CipherText -> m StrictByteString
+ Sel.SecretKey.Stream: decryptChunk :: MonadIO m => Multipart s -> Maybe AdditionalData -> Ciphertext -> m StrictByteString
- Sel.SecretKey.Stream: decryptList :: forall m. MonadIO m => SecretKey -> Header -> [CipherText] -> m (Maybe [StrictByteString])
+ Sel.SecretKey.Stream: decryptList :: MonadIO m => SecretKey -> Header -> [(Maybe AdditionalData, Ciphertext)] -> m (Maybe [StrictByteString])
- Sel.SecretKey.Stream: decryptStream :: forall (a :: Type) (m :: Type -> Type). MonadIO m => SecretKey -> Header -> (forall s. Multipart s -> m a) -> m (Maybe a)
+ Sel.SecretKey.Stream: decryptStream :: forall a m. MonadIO m => SecretKey -> Header -> (forall s. () => Multipart s -> m a) -> m (Maybe a)
- Sel.SecretKey.Stream: encryptChunk :: forall m s. MonadIO m => Multipart s -> MessageTag -> StrictByteString -> m CipherText
+ Sel.SecretKey.Stream: encryptChunk :: MonadIO m => Multipart s -> MessageTag -> Maybe AdditionalData -> StrictByteString -> m Ciphertext
- Sel.SecretKey.Stream: encryptList :: forall m. MonadIO m => SecretKey -> [StrictByteString] -> m (Header, [CipherText])
+ Sel.SecretKey.Stream: encryptList :: MonadIO m => SecretKey -> [(Maybe AdditionalData, StrictByteString)] -> m (Header, [Ciphertext])
- Sel.SecretKey.Stream: encryptStream :: forall (a :: Type) (m :: Type -> Type). MonadIO m => SecretKey -> (forall s. Multipart s -> m a) -> m (Header, a)
+ Sel.SecretKey.Stream: encryptStream :: forall a m. MonadIO m => SecretKey -> (forall s. () => Multipart s -> m a) -> m (Header, a)
Files
- CHANGELOG.md +17/−0
- sel.cabal +65/−46
- src/Sel/HMAC/SHA256.hs +16/−22
- src/Sel/HMAC/SHA512.hs +16/−22
- src/Sel/HMAC/SHA512_256.hs +16/−21
- src/Sel/Hashing.hs +15/−18
- src/Sel/Hashing/Password.hs +19/−14
- src/Sel/Hashing/SHA256.hs +13/−13
- src/Sel/Hashing/SHA512.hs +13/−13
- src/Sel/Hashing/Short.hs +27/−26
- src/Sel/Internal.hs +98/−36
- src/Sel/Internal/Scoped.hs +84/−0
- src/Sel/Internal/Scoped/Foreign.hs +47/−0
- src/Sel/Internal/Sodium.hs +26/−0
- src/Sel/PublicKey/Cipher.hs +77/−98
- src/Sel/PublicKey/Seal.hs +25/−15
- src/Sel/PublicKey/Signature.hs +27/−18
- src/Sel/Scrypt.hs +13/−8
- src/Sel/SecretKey/Authentication.hs +15/−27
- src/Sel/SecretKey/Cipher.hs +86/−86
- src/Sel/SecretKey/Stream.hs +175/−116
- test/Test/HMAC.hs +12/−11
- test/Test/Hashing.hs +4/−4
- test/Test/Hashing/Password.hs +5/−4
- test/Test/Hashing/SHA2.hs +5/−4
- test/Test/Hashing/Short.hs +3/−2
- test/Test/PublicKey/Cipher.hs +9/−8
- test/Test/PublicKey/Seal.hs +3/−2
- test/Test/PublicKey/Signature.hs +2/−1
- test/Test/Scrypt.hs +2/−1
- test/Test/SecretKey/Authentication.hs +5/−4
- test/Test/SecretKey/Cipher.hs +8/−7
- test/Test/SecretKey/Stream.hs +14/−9
CHANGELOG.md view
@@ -1,5 +1,22 @@ # CHANGELOG +## sel-0.1.0.0++* Consistently rename ciphertext-related types and functions [#182](https://github.com/haskell-cryptography/libsodium-bindings/pull/182)+* Add support for additional data (AD) in `Sel.SecretKey.Stream` [#183](https://github.com/haskell-cryptography/libsodium-bindings/pull/183)+* Define some cryptographic functions in sel as pure [#189](https://github.com/haskell-cryptography/libsodium-bindings/pull/189)++## sel-0.0.3.0++* Add constant time hex encoding [#176](https://github.com/haskell-cryptography/libsodium-bindings/pull/176)+* Support `text-display` 1.0.0.0+* Replace usages of `memcpy` with `Foreign.copyBytes` [#172](https://github.com/haskell-cryptography/libsodium-bindings/pull/172)+* Add constant-time pointer comparison [#171](https://github.com/haskell-cryptography/libsodium-bindings/pull/171)+* (Internal) Add constant-time Eq, use Scoped for internals [#169](https://github.com/haskell-cryptography/libsodium-bindings/pull/169)+* Cleanup, allow more versions of `tasty` [#168](https://github.com/haskell-cryptography/libsodium-bindings/pull/168)+* (Internal) Add `Scoped` for better readability of nested continuations [#167](https://github.com/haskell-cryptography/libsodium-bindings/pull/167)+* Update hedgehog [#180](https://github.com/haskell-cryptography/libsodium-bindings/pull/180)+ ## sel-0.0.2.0 * Add usages of `secureMain` in examples
sel.cabal view
@@ -1,59 +1,71 @@-cabal-version: 3.0-name: sel-version: 0.0.2.0-category: Cryptography-synopsis: Cryptography for the casual user+cabal-version: 3.0+name: sel+version: 0.1.0.0+category: Cryptography+synopsis: Cryptography for the casual user description: The high-level library aimed at casual users of cryptography, by the Haskell Cryptography Group -homepage: https://github.com/haskell-cryptography/libsodium-bindings+homepage: https://github.com/haskell-cryptography/libsodium-bindings bug-reports: https://github.com/haskell-cryptography/libsodium-bindings/issues -author: Hécate Moonlight, Koz Ross-maintainer: The Haskell Cryptography contributors-license: BSD-3-Clause-build-type: Simple+author: Hécate Moonlight, Koz Ross+maintainer: The Haskell Cryptography contributors+license: BSD-3-Clause+build-type: Simple tested-with:- GHC ==9.2.8 || ==9.4.8 || ==9.6.6 || ==9.8.2 || ==9.10.1+ GHC ==9.2.8 || ==9.4.8 || ==9.6.6 || ==9.8.2 || ==9.10.1 || ==9.12.1 extra-source-files: LICENSE README.md -extra-doc-files: CHANGELOG.md+extra-doc-files: CHANGELOG.md source-repository head- type: git+ type: git location: https://github.com/haskell-cryptography/libsodium-bindings+ subdir: sel common common ghc-options:- -Wall -Wcompat -Widentities -Wincomplete-record-updates- -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints- -fhide-source-paths -Wno-unused-do-bind -fshow-hole-constraints- -fprint-potential-instances -Wno-unticked-promoted-constructors+ -Wall+ -Wcompat+ -Widentities+ -Wincomplete-record-updates+ -Wincomplete-uni-patterns+ -Wpartial-fields+ -Wredundant-constraints+ -fhide-source-paths+ -Wno-unused-do-bind+ -fshow-hole-constraints+ -fprint-potential-instances+ -Wno-unticked-promoted-constructors -Werror=unused-imports default-language: Haskell2010 common test-options- ghc-options: -rtsopts -threaded -with-rtsopts=-N+ ghc-options:+ -rtsopts+ -threaded+ -with-rtsopts=-N library- import: common- hs-source-dirs: src+ import: common+ hs-source-dirs: src exposed-modules: Sel+ Sel.HMAC+ Sel.HMAC.SHA256+ Sel.HMAC.SHA512+ Sel.HMAC.SHA512_256 Sel.Hashing Sel.Hashing.Password Sel.Hashing.SHA256 Sel.Hashing.SHA512 Sel.Hashing.Short- Sel.HMAC- Sel.HMAC.SHA256- Sel.HMAC.SHA512- Sel.HMAC.SHA512_256 Sel.PublicKey.Cipher Sel.PublicKey.Seal Sel.PublicKey.Signature@@ -62,26 +74,33 @@ Sel.SecretKey.Cipher Sel.SecretKey.Stream - other-modules: Sel.Internal+ other-modules:+ Sel.Internal+ Sel.Internal.Scoped+ Sel.Internal.Scoped.Foreign+ Sel.Internal.Sodium+ build-depends:- , base >=4.14 && <5- , base16 ^>=1.0- , bytestring >=0.10 && <0.13- , libsodium-bindings ^>=0.0.2- , text >=1.2 && <2.2- , text-display ^>=0.0+ base >=4.14 && <5,+ base16 ^>=1.0,+ bytestring >=0.10 && <0.13,+ libsodium-bindings ^>=0.0.3,+ text >=1.2 && <2.2,+ text-builder-linear ^>=0.1,+ text-display ^>=1.0,+ transformers ^>=0.6.0, test-suite sel-tests- import: common- import: test-options- type: exitcode-stdio-1.0- main-is: Main.hs+ import: common+ import: test-options+ type: exitcode-stdio-1.0+ main-is: Main.hs other-modules:+ Test.HMAC Test.Hashing Test.Hashing.Password Test.Hashing.SHA2 Test.Hashing.Short- Test.HMAC Test.PublicKey.Cipher Test.PublicKey.Seal Test.PublicKey.Signature@@ -93,13 +112,13 @@ hs-source-dirs: test build-depends:- , base- , base16- , bytestring- , hedgehog ^>=1.4- , libsodium-bindings- , sel- , tasty ^>=1.5- , tasty-hunit ^>=0.10- , text- , text-display+ base,+ base16,+ bytestring,+ hedgehog >=1.4,+ libsodium-bindings,+ sel,+ tasty >=1.4 && <1.6,+ tasty-hunit ^>=0.10,+ text,+ text-display,
src/Sel/HMAC/SHA256.hs view
@@ -52,7 +52,6 @@ import Control.Monad (void, when) import Control.Monad.IO.Class (MonadIO, liftIO)-import qualified Data.Base16.Types as Base16 import Data.ByteString (StrictByteString) import qualified Data.ByteString as BS import qualified Data.ByteString.Base16 as Base16@@ -66,8 +65,6 @@ import qualified Foreign import Foreign.C (CChar, CSize, CUChar, CULLong) import Foreign.C.Error (throwErrno)-import System.IO.Unsafe (unsafeDupablePerformIO)- import LibSodium.Bindings.SHA2 ( CryptoAuthHMACSHA256State , cryptoAuthHMACSHA256@@ -81,8 +78,11 @@ , cryptoAuthHMACSHA256Verify ) import LibSodium.Bindings.SecureMemory (finalizerSodiumFree, sodiumMalloc)-import Sel.Internal (allocateWith, foreignPtrEq, foreignPtrOrd)+import System.IO.Unsafe (unsafeDupablePerformIO) +import Sel.Internal (allocateWith, foreignPtrEqConstantTime, foreignPtrOrdConstantTime)+import Sel.Internal.Sodium (binaryToHex)+ -- $introduction -- The 'authenticate' function computes an authentication tag for a message and a secret key, -- and provides a way to verify that a given tag is valid for a given message and a key.@@ -117,9 +117,9 @@ -- ^ Message to authenticate -> AuthenticationKey -- ^ Secret key for authentication- -> IO AuthenticationTag+ -> AuthenticationTag -- ^ Cryptographic tag for authentication-authenticate message (AuthenticationKey authenticationKeyForeignPtr) =+authenticate message (AuthenticationKey authenticationKeyForeignPtr) = unsafeDupablePerformIO $ BS.unsafeUseAsCStringLen message $ \(cString, cStringLen) -> do authenticationTagForeignPtr <- Foreign.mallocForeignPtrBytes@@ -244,16 +244,14 @@ -- @since 0.0.1.0 instance Eq AuthenticationKey where (AuthenticationKey hk1) == (AuthenticationKey hk2) =- unsafeDupablePerformIO $- foreignPtrEq hk1 hk2 cryptoAuthHMACSHA256KeyBytes+ foreignPtrEqConstantTime hk1 hk2 cryptoAuthHMACSHA256KeyBytes -- | -- -- @since 0.0.1.0 instance Ord AuthenticationKey where compare (AuthenticationKey hk1) (AuthenticationKey hk2) =- unsafeDupablePerformIO $- foreignPtrOrd hk1 hk2 cryptoAuthHMACSHA256KeyBytes+ foreignPtrOrdConstantTime hk1 hk2 cryptoAuthHMACSHA256KeyBytes -- | > show authenticationKey == "[REDACTED]" --@@ -318,14 +316,14 @@ (Foreign.castForeignPtr @CUChar @Word8 authenticationKeyForeignPtr) (fromIntegral @CSize @Int cryptoAuthHMACSHA256KeyBytes) --- | Convert a 'AuthenticationKey to a hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'AuthenticationKey to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- ⚠️ Be prudent as to where you store it! -- -- @since 0.0.1.0 unsafeAuthenticationKeyToHexByteString :: AuthenticationKey -> StrictByteString-unsafeAuthenticationKeyToHexByteString =- Base16.extractBase16 . Base16.encodeBase16' . unsafeAuthenticationKeyToBinary+unsafeAuthenticationKeyToHexByteString (AuthenticationKey authenticationKeyForeignPtr) =+ binaryToHex authenticationKeyForeignPtr cryptoAuthHMACSHA256KeyBytes -- | A secret authentication key of size 'cryptoAuthHMACSHA256Bytes'. --@@ -342,16 +340,14 @@ -- @since 0.0.1.0 instance Eq AuthenticationTag where (AuthenticationTag hk1) == (AuthenticationTag hk2) =- unsafeDupablePerformIO $- foreignPtrEq hk1 hk2 cryptoAuthHMACSHA256Bytes+ foreignPtrEqConstantTime hk1 hk2 cryptoAuthHMACSHA256Bytes -- | -- -- @since 0.0.1.0 instance Ord AuthenticationTag where compare (AuthenticationTag hk1) (AuthenticationTag hk2) =- unsafeDupablePerformIO $- foreignPtrOrd hk1 hk2 cryptoAuthHMACSHA256Bytes+ foreignPtrOrdConstantTime hk1 hk2 cryptoAuthHMACSHA256Bytes -- | --@@ -359,14 +355,12 @@ instance Show AuthenticationTag where show = BS.unpackChars . authenticationTagToHexByteString --- | Convert an 'AuthenticationTag' to a hexadecimal-encoded 'StrictByteString'.+-- | Convert an 'AuthenticationTag' to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- @since 0.0.1.0 authenticationTagToHexByteString :: AuthenticationTag -> StrictByteString-authenticationTagToHexByteString authenticationTag =- Base16.extractBase16 $- Base16.encodeBase16' $- authenticationTagToBinary authenticationTag+authenticationTagToHexByteString (AuthenticationTag authenticationTagForeignPtr) =+ binaryToHex authenticationTagForeignPtr cryptoAuthHMACSHA256Bytes -- | Convert an 'AuthenticationTag' to a binary 'StrictByteString'. --
src/Sel/HMAC/SHA512.hs view
@@ -52,7 +52,6 @@ import Control.Monad (void, when) import Control.Monad.IO.Class (MonadIO, liftIO)-import qualified Data.Base16.Types as Base16 import Data.ByteString (StrictByteString) import qualified Data.ByteString as BS import qualified Data.ByteString.Base16 as Base16@@ -66,8 +65,6 @@ import qualified Foreign import Foreign.C (CChar, CSize, CUChar, CULLong) import Foreign.C.Error (throwErrno)-import System.IO.Unsafe (unsafeDupablePerformIO)- import LibSodium.Bindings.SHA2 ( CryptoAuthHMACSHA512State , cryptoAuthHMACSHA512@@ -81,8 +78,11 @@ , cryptoAuthHMACSHA512Verify ) import LibSodium.Bindings.SecureMemory (finalizerSodiumFree, sodiumMalloc)-import Sel.Internal (allocateWith, foreignPtrEq, foreignPtrOrd)+import System.IO.Unsafe (unsafeDupablePerformIO) +import Sel.Internal (allocateWith, foreignPtrEqConstantTime, foreignPtrOrdConstantTime)+import Sel.Internal.Sodium (binaryToHex)+ -- $introduction -- The 'authenticate' function computes an authentication tag for a message and a secret key, -- and provides a way to verify that a given tag is valid for a given message and a key.@@ -117,9 +117,9 @@ -- ^ Message to authenticate -> AuthenticationKey -- ^ Secret key for authentication- -> IO AuthenticationTag+ -> AuthenticationTag -- ^ Cryptographic tag for authentication-authenticate message (AuthenticationKey authenticationKeyForeignPtr) =+authenticate message (AuthenticationKey authenticationKeyForeignPtr) = unsafeDupablePerformIO $ BS.unsafeUseAsCStringLen message $ \(cString, cStringLen) -> do authenticationTagForeignPtr <- Foreign.mallocForeignPtrBytes@@ -244,16 +244,14 @@ -- @since 0.0.1.0 instance Eq AuthenticationKey where (AuthenticationKey hk1) == (AuthenticationKey hk2) =- unsafeDupablePerformIO $- foreignPtrEq hk1 hk2 cryptoAuthHMACSHA512KeyBytes+ foreignPtrEqConstantTime hk1 hk2 cryptoAuthHMACSHA512KeyBytes -- | -- -- @since 0.0.1.0 instance Ord AuthenticationKey where compare (AuthenticationKey hk1) (AuthenticationKey hk2) =- unsafeDupablePerformIO $- foreignPtrOrd hk1 hk2 cryptoAuthHMACSHA512KeyBytes+ foreignPtrOrdConstantTime hk1 hk2 cryptoAuthHMACSHA512KeyBytes -- | > show authenticationKey == "[REDACTED]" --@@ -318,14 +316,14 @@ (Foreign.castForeignPtr @CUChar @Word8 authenticationKeyForeignPtr) (fromIntegral @CSize @Int cryptoAuthHMACSHA512KeyBytes) --- | Convert a 'AuthenticationKey to a hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'AuthenticationKey to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- ⚠️ Be prudent as to where you store it! -- -- @since 0.0.1.0 unsafeAuthenticationKeyToHexByteString :: AuthenticationKey -> StrictByteString-unsafeAuthenticationKeyToHexByteString =- Base16.extractBase16 . Base16.encodeBase16' . unsafeAuthenticationKeyToBinary+unsafeAuthenticationKeyToHexByteString (AuthenticationKey authenticationKeyForeignPtr) =+ binaryToHex authenticationKeyForeignPtr cryptoAuthHMACSHA512KeyBytes -- | A secret authentication key of size 'cryptoAuthHMACSHA512Bytes'. --@@ -342,16 +340,14 @@ -- @since 0.0.1.0 instance Eq AuthenticationTag where (AuthenticationTag hk1) == (AuthenticationTag hk2) =- unsafeDupablePerformIO $- foreignPtrEq hk1 hk2 cryptoAuthHMACSHA512Bytes+ foreignPtrEqConstantTime hk1 hk2 cryptoAuthHMACSHA512Bytes -- | -- -- @since 0.0.1.0 instance Ord AuthenticationTag where compare (AuthenticationTag hk1) (AuthenticationTag hk2) =- unsafeDupablePerformIO $- foreignPtrOrd hk1 hk2 cryptoAuthHMACSHA512Bytes+ foreignPtrOrdConstantTime hk1 hk2 cryptoAuthHMACSHA512Bytes -- | --@@ -359,14 +355,12 @@ instance Show AuthenticationTag where show = BS.unpackChars . authenticationTagToHexByteString --- | Convert an 'AuthenticationTag' to a hexadecimal-encoded 'StrictByteString'.+-- | Convert an 'AuthenticationTag' to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- @since 0.0.1.0 authenticationTagToHexByteString :: AuthenticationTag -> StrictByteString-authenticationTagToHexByteString authenticationTag =- Base16.extractBase16 $- Base16.encodeBase16' $- authenticationTagToBinary authenticationTag+authenticationTagToHexByteString (AuthenticationTag authenticationTagForeignPtr) =+ binaryToHex authenticationTagForeignPtr cryptoAuthHMACSHA512Bytes -- | Convert an 'AuthenticationTag' to a binary 'StrictByteString'. --
src/Sel/HMAC/SHA512_256.hs view
@@ -64,8 +64,6 @@ import qualified Foreign import Foreign.C (CChar, CSize, CUChar, CULLong) import Foreign.C.Error (throwErrno)-import System.IO.Unsafe (unsafeDupablePerformIO)- import LibSodium.Bindings.SHA2 ( CryptoAuthHMACSHA512256State , cryptoAuthHMACSHA512256@@ -79,8 +77,11 @@ , cryptoAuthHMACSHA512256Verify ) import LibSodium.Bindings.SecureMemory (finalizerSodiumFree, sodiumMalloc)-import Sel.Internal (allocateWith, foreignPtrEq, foreignPtrOrd)+import System.IO.Unsafe (unsafeDupablePerformIO) +import Sel.Internal (allocateWith, foreignPtrEqConstantTime, foreignPtrOrdConstantTime)+import Sel.Internal.Sodium (binaryToHex)+ -- $introduction -- The 'authenticate' function computes an authentication tag for a message and a secret key, -- and provides a way to verify that a given tag is valid for a given message and a key.@@ -115,9 +116,9 @@ -- ^ Message to authenticate -> AuthenticationKey -- ^ Secret key for authentication- -> IO AuthenticationTag+ -> AuthenticationTag -- ^ Cryptographic tag for authentication-authenticate message (AuthenticationKey authenticationKeyForeignPtr) =+authenticate message (AuthenticationKey authenticationKeyForeignPtr) = unsafeDupablePerformIO $ BS.unsafeUseAsCStringLen message $ \(cString, cStringLen) -> do authenticationTagForeignPtr <- Foreign.mallocForeignPtrBytes@@ -242,16 +243,14 @@ -- @since 0.0.1.0 instance Eq AuthenticationKey where (AuthenticationKey hk1) == (AuthenticationKey hk2) =- unsafeDupablePerformIO $- foreignPtrEq hk1 hk2 cryptoAuthHMACSHA512256KeyBytes+ foreignPtrEqConstantTime hk1 hk2 cryptoAuthHMACSHA512256KeyBytes -- | -- -- @since 0.0.1.0 instance Ord AuthenticationKey where compare (AuthenticationKey hk1) (AuthenticationKey hk2) =- unsafeDupablePerformIO $- foreignPtrOrd hk1 hk2 cryptoAuthHMACSHA512256KeyBytes+ foreignPtrOrdConstantTime hk1 hk2 cryptoAuthHMACSHA512256KeyBytes -- | > show authenticationKey == "[REDACTED]" --@@ -317,14 +316,14 @@ (Foreign.castForeignPtr @CUChar @Word8 authenticationKeyForeignPtr) (fromIntegral @CSize @Int cryptoAuthHMACSHA512256KeyBytes) --- | Convert a 'AuthenticationKey to a hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'AuthenticationKey to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- ⚠️ Be prudent as to where you store it! -- -- @since 0.0.1.0 unsafeAuthenticationKeyToHexByteString :: AuthenticationKey -> StrictByteString-unsafeAuthenticationKeyToHexByteString =- Base16.extractBase16 . Base16.encodeBase16' . unsafeAuthenticationKeyToBinary+unsafeAuthenticationKeyToHexByteString (AuthenticationKey authenticationKeyForeignPtr) =+ binaryToHex authenticationKeyForeignPtr cryptoAuthHMACSHA512256KeyBytes -- | A secret authentication key of size 'cryptoAuthHMACSHA512256Bytes'. --@@ -341,16 +340,14 @@ -- @since 0.0.1.0 instance Eq AuthenticationTag where (AuthenticationTag hk1) == (AuthenticationTag hk2) =- unsafeDupablePerformIO $- foreignPtrEq hk1 hk2 cryptoAuthHMACSHA512256Bytes+ foreignPtrEqConstantTime hk1 hk2 cryptoAuthHMACSHA512256Bytes -- | -- -- @since 0.0.1.0 instance Ord AuthenticationTag where compare (AuthenticationTag hk1) (AuthenticationTag hk2) =- unsafeDupablePerformIO $- foreignPtrOrd hk1 hk2 cryptoAuthHMACSHA512256Bytes+ foreignPtrOrdConstantTime hk1 hk2 cryptoAuthHMACSHA512256Bytes -- | --@@ -358,14 +355,12 @@ instance Show AuthenticationTag where show = BS.unpackChars . authenticationTagToHexByteString --- | Convert an 'AuthenticationTag' to a hexadecimal-encoded 'StrictByteString'.+-- | Convert an 'AuthenticationTag' to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- @since 0.0.1.0 authenticationTagToHexByteString :: AuthenticationTag -> StrictByteString-authenticationTagToHexByteString authenticationTag =- Base16.extractBase16 $- Base16.encodeBase16' $- authenticationTagToBinary authenticationTag+authenticationTagToHexByteString (AuthenticationTag authenticationTagForeignPtr) =+ binaryToHex authenticationTagForeignPtr cryptoAuthHMACSHA512256Bytes -- | Convert an 'AuthenticationTag' to a binary 'StrictByteString'. --
src/Sel/Hashing.hs view
@@ -36,24 +36,22 @@ where import Control.Monad (void)+import Control.Monad.IO.Class (MonadIO, liftIO)+import qualified Data.Base16.Types as Base16 import Data.ByteString (StrictByteString) import qualified Data.ByteString.Base16 as Base16 import qualified Data.ByteString.Internal as BS import Data.ByteString.Unsafe (unsafeUseAsCStringLen) import qualified Data.ByteString.Unsafe as BS+import Data.Kind (Type) import Data.Text (Text)+import qualified Data.Text.Builder.Linear as Builder import Data.Text.Display-import qualified Data.Text.Lazy.Builder as Builder import Foreign (Ptr) import qualified Foreign import Foreign.C (CChar, CInt, CSize, CUChar, CULLong) import Foreign.ForeignPtr import Foreign.Storable-import System.IO.Unsafe (unsafeDupablePerformIO)--import Control.Monad.IO.Class (MonadIO, liftIO)-import qualified Data.Base16.Types as Base16-import Data.Kind (Type) import LibSodium.Bindings.GenericHashing ( CryptoGenericHashState , cryptoGenericHash@@ -65,7 +63,10 @@ , cryptoGenericHashStateBytes , cryptoGenericHashUpdate )+import System.IO.Unsafe (unsafeDupablePerformIO)+ import Sel.Internal+import Sel.Internal.Sodium (binaryToHex) -- $introduction --@@ -94,13 +95,11 @@ instance Eq HashKey where (HashKey hk1) == (HashKey hk2) =- unsafeDupablePerformIO $- foreignPtrEq hk1 hk2 cryptoGenericHashKeyBytes+ foreignPtrEq hk1 hk2 cryptoGenericHashKeyBytes instance Ord HashKey where compare (HashKey hk1) (HashKey hk2) =- unsafeDupablePerformIO $- foreignPtrOrd hk1 hk2 cryptoGenericHashKeyBytes+ foreignPtrOrd hk1 hk2 cryptoGenericHashKeyBytes -- | Create a new 'HashKey' of size 'cryptoGenericHashKeyBytes'. --@@ -127,16 +126,14 @@ -- @since 0.0.1.0 instance Eq Hash where (Hash h1) == (Hash h2) =- unsafeDupablePerformIO $- foreignPtrEq h1 h2 cryptoGenericHashBytes+ foreignPtrEq h1 h2 cryptoGenericHashBytes -- | -- -- @since 0.0.1.0 instance Ord Hash where compare (Hash h1) (Hash h2) =- unsafeDupablePerformIO $- foreignPtrOrd h1 h2 cryptoGenericHashBytes+ foreignPtrOrd h1 h2 cryptoGenericHashBytes -- | --@@ -178,8 +175,8 @@ -- Without a 'HashKey', hashing the same data twice will give the same result. -- -- @since 0.0.1.0-hashByteString :: Maybe HashKey -> StrictByteString -> IO Hash-hashByteString mHashKey bytestring =+hashByteString :: Maybe HashKey -> StrictByteString -> Hash+hashByteString mHashKey bytestring = unsafeDupablePerformIO $ case mHashKey of Just (HashKey fPtr) -> Foreign.withForeignPtr fPtr $ \keyPtr ->@@ -208,11 +205,11 @@ hashToHexText :: Hash -> Text hashToHexText = Base16.extractBase16 . Base16.encodeBase16 . hashToBinary --- | Convert a 'Hash' to a strict, hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'Hash' to a strict, hexadecimal-encoded 'StrictByteString' in constant time. -- -- @since 0.0.1.0 hashToHexByteString :: Hash -> StrictByteString-hashToHexByteString = Base16.extractBase16 . Base16.encodeBase16' . hashToBinary+hashToHexByteString (Hash hashPtr) = binaryToHex hashPtr cryptoGenericHashBytes -- | Convert a 'Hash' to a strict binary 'StrictByteString'. --
src/Sel/Hashing/Password.hs view
@@ -53,6 +53,7 @@ where import Control.Monad (void)+import qualified Data.Base16.Types as Base16 import Data.ByteString (StrictByteString) import qualified Data.ByteString as BS import qualified Data.ByteString.Base16 as Base16@@ -60,20 +61,19 @@ import qualified Data.ByteString.Internal as BS import qualified Data.ByteString.Unsafe as BS import Data.Text (Text)+import qualified Data.Text.Builder.Linear as Builder import Data.Text.Display import qualified Data.Text.Encoding as Text-import qualified Data.Text.Lazy.Builder as Builder import Foreign hiding (void) import Foreign.C-import System.IO.Unsafe (unsafeDupablePerformIO)--import Sel.Internal--import qualified Data.Base16.Types as Base16 import GHC.Generics import LibSodium.Bindings.PasswordHashing import LibSodium.Bindings.Random+import System.IO.Unsafe (unsafeDupablePerformIO) +import Sel.Internal+import Sel.Internal.Sodium (binaryToHex)+ -- $introduction -- -- This API provides functions for password hashing, backed by the [Argon2id](https://en.wikipedia.org/wiki/Argon2) algorithm.@@ -94,14 +94,18 @@ -- | @since 0.0.1.0 instance Eq PasswordHash where (PasswordHash ph1) == (PasswordHash ph2) =- unsafeDupablePerformIO $- foreignPtrEq ph1 ph2 cryptoPWHashStrBytes+ foreignPtrEq+ (Foreign.castForeignPtr @CChar @CUChar ph1)+ (Foreign.castForeignPtr @CChar @CUChar ph2)+ cryptoPWHashStrBytes -- | @since 0.0.1.0 instance Ord PasswordHash where (PasswordHash ph1) `compare` (PasswordHash ph2) =- unsafeDupablePerformIO $- foreignPtrOrd ph1 ph2 cryptoPWHashStrBytes+ foreignPtrOrd+ (Foreign.castForeignPtr @CChar @CUChar ph1)+ (Foreign.castForeignPtr @CChar @CUChar ph2)+ cryptoPWHashStrBytes -- | @since 0.0.1.0 instance Show PasswordHash where@@ -148,8 +152,8 @@ -- The hash is __not__ encoded in human-readable format. -- -- @since 0.0.1.0-hashByteStringWithParams :: Argon2Params -> Salt -> StrictByteString -> IO PasswordHash-hashByteStringWithParams Argon2Params{opsLimit, memLimit} (Salt argonSalt) bytestring =+hashByteStringWithParams :: Argon2Params -> Salt -> StrictByteString -> PasswordHash+hashByteStringWithParams Argon2Params{opsLimit, memLimit} (Salt argonSalt) bytestring = unsafeDupablePerformIO $ BS.unsafeUseAsCStringLen bytestring $ \(cString, cStringLen) -> do BS.unsafeUseAsCStringLen argonSalt $ \(saltString, _) -> do hashForeignPtr <- mallocForeignPtrBytes (fromIntegral cryptoPWHashStrBytes)@@ -212,13 +216,14 @@ let !errPos = BS.length bs - BS.length suffix in error $ "decodeASCII: detected non-ASCII codepoint " ++ show word ++ " at position " ++ show errPos <> ". " <> show bs --- | Convert a 'PasswordHash' to a hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'PasswordHash' to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- It is recommended to use this one on a 'PasswordHash' produced by 'hashByteStringWithParams'. -- -- @since 0.0.1.0 passwordHashToHexByteString :: PasswordHash -> StrictByteString-passwordHashToHexByteString = Base16.extractBase16 . Base16.encodeBase16' . passwordHashToByteString+passwordHashToHexByteString (PasswordHash passwordHashForeignPtr) =+ binaryToHex (Foreign.castForeignPtr @CChar @CUChar passwordHashForeignPtr) cryptoPWHashStrBytes -- | Convert a 'PasswordHash' to a strict hexadecimal-encoded 'Text'. --
src/Sel/Hashing/SHA256.hs view
@@ -33,14 +33,17 @@ ) where import Control.Monad (void)+import Control.Monad.IO.Class (MonadIO (liftIO))+import qualified Data.Base16.Types as Base16 import Data.ByteString (StrictByteString) import qualified Data.ByteString.Base16 as Base16 import qualified Data.ByteString.Internal as BS import qualified Data.ByteString.Unsafe as BS+import Data.Kind (Type) import Data.Text (Text)+import qualified Data.Text.Builder.Linear as Builder import Data.Text.Display (Display (..)) import qualified Data.Text.Encoding as Text-import qualified Data.Text.Internal.Builder as Builder import Foreign (ForeignPtr, Ptr, Storable) import qualified Foreign import Foreign.C (CChar, CSize, CUChar, CULLong)@@ -55,10 +58,8 @@ ) import System.IO.Unsafe (unsafeDupablePerformIO) -import Control.Monad.IO.Class (MonadIO (liftIO))-import qualified Data.Base16.Types as Base16-import Data.Kind (Type) import Sel.Internal+import Sel.Internal.Sodium (binaryToHex) -- $usage --@@ -83,16 +84,14 @@ -- @since 0.0.1.0 instance Eq Hash where (Hash h1) == (Hash h2) =- unsafeDupablePerformIO $- foreignPtrEq h1 h2 cryptoHashSHA256Bytes+ foreignPtrEq h1 h2 cryptoHashSHA256Bytes -- | -- -- @since 0.0.1.0 instance Ord Hash where compare (Hash h1) (Hash h2) =- unsafeDupablePerformIO $- foreignPtrOrd h1 h2 cryptoHashSHA256Bytes+ foreignPtrOrd h1 h2 cryptoHashSHA256Bytes -- | --@@ -132,8 +131,8 @@ -- | Hash a 'StrictByteString' with the SHA-256 algorithm. -- -- @since 0.0.1.0-hashByteString :: StrictByteString -> IO Hash-hashByteString bytestring =+hashByteString :: StrictByteString -> Hash+hashByteString bytestring = unsafeDupablePerformIO $ BS.unsafeUseAsCStringLen bytestring $ \(cString, cStringLen) -> do hashForeignPtr <- Foreign.mallocForeignPtrBytes (fromIntegral cryptoHashSHA256Bytes) Foreign.withForeignPtr hashForeignPtr $ \hashPtr ->@@ -147,7 +146,7 @@ -- | Hash a UTF8-encoded strict 'Text' with the SHA-256 algorithm. -- -- @since 0.0.1.0-hashText :: Text -> IO Hash+hashText :: Text -> Hash hashText text = hashByteString (Text.encodeUtf8 text) -- == Displaying@@ -158,11 +157,12 @@ hashToHexText :: Hash -> Text hashToHexText = Base16.extractBase16 . Base16.encodeBase16 . hashToBinary --- | Convert a 'Hash' to a strict, hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'Hash' to a strict, hexadecimal-encoded 'StrictByteString' in constant time. -- -- @since 0.0.1.0 hashToHexByteString :: Hash -> StrictByteString-hashToHexByteString = Base16.extractBase16 . Base16.encodeBase16' . hashToBinary+hashToHexByteString (Hash hashForeignPtr) =+ binaryToHex hashForeignPtr cryptoHashSHA256Bytes -- | Convert a 'Hash' to a binary 'StrictByteString'. --
src/Sel/Hashing/SHA512.hs view
@@ -33,14 +33,17 @@ ) where import Control.Monad (void)+import Control.Monad.IO.Class (MonadIO, liftIO)+import qualified Data.Base16.Types as Base16 import Data.ByteString (StrictByteString) import qualified Data.ByteString.Base16 as Base16 import qualified Data.ByteString.Internal as BS import qualified Data.ByteString.Unsafe as BS+import Data.Kind (Type) import Data.Text (Text)+import qualified Data.Text.Builder.Linear as Builder import Data.Text.Display (Display (..)) import qualified Data.Text.Encoding as Text-import qualified Data.Text.Internal.Builder as Builder import Foreign (ForeignPtr, Ptr, Storable) import qualified Foreign import Foreign.C (CChar, CSize, CUChar, CULLong)@@ -55,10 +58,8 @@ ) import System.IO.Unsafe (unsafeDupablePerformIO) -import Control.Monad.IO.Class (MonadIO, liftIO)-import qualified Data.Base16.Types as Base16-import Data.Kind (Type) import Sel.Internal+import Sel.Internal.Sodium (binaryToHex) -- $usage --@@ -83,16 +84,14 @@ -- @since 0.0.1.0 instance Eq Hash where (Hash h1) == (Hash h2) =- unsafeDupablePerformIO $- foreignPtrEq h1 h2 cryptoHashSHA512Bytes+ foreignPtrEq h1 h2 cryptoHashSHA512Bytes -- | -- -- @since 0.0.1.0 instance Ord Hash where compare (Hash h1) (Hash h2) =- unsafeDupablePerformIO $- foreignPtrOrd h1 h2 cryptoHashSHA512Bytes+ foreignPtrOrd h1 h2 cryptoHashSHA512Bytes -- | --@@ -137,11 +136,12 @@ hashToHexText :: Hash -> Text hashToHexText = Base16.extractBase16 . Base16.encodeBase16 . hashToBinary --- | Convert a 'Hash' to a strict, hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'Hash' to a strict, hexadecimal-encoded 'StrictByteString' in constant time. -- -- @since 0.0.1.0 hashToHexByteString :: Hash -> StrictByteString-hashToHexByteString = Base16.extractBase16 . Base16.encodeBase16' . hashToBinary+hashToHexByteString (Hash hashForeignPtr) =+ binaryToHex hashForeignPtr cryptoHashSHA512Bytes -- | Convert a 'Hash' to a binary 'StrictByteString'. --@@ -156,8 +156,8 @@ -- | Hash a 'StrictByteString' with the SHA-512 algorithm. -- -- @since 0.0.1.0-hashByteString :: StrictByteString -> IO Hash-hashByteString bytestring =+hashByteString :: StrictByteString -> Hash+hashByteString bytestring = unsafeDupablePerformIO $ BS.unsafeUseAsCStringLen bytestring $ \(cString, cStringLen) -> do hashForeignPtr <- Foreign.mallocForeignPtrBytes (fromIntegral cryptoHashSHA512Bytes) Foreign.withForeignPtr hashForeignPtr $ \hashPtr ->@@ -171,7 +171,7 @@ -- | Hash a UTF8-encoded strict 'Text' with the SHA-512 algorithm. -- -- @since 0.0.1.0-hashText :: Text -> IO Hash+hashText :: Text -> Hash hashText text = hashByteString (Text.encodeUtf8 text) -- ** Hashing a multi-parts message
src/Sel/Hashing/Short.hs view
@@ -43,6 +43,7 @@ import Control.Exception (throw) import Control.Monad (void, when)+import qualified Data.Base16.Types as Base16 import Data.ByteString (StrictByteString) import qualified Data.ByteString as BS import qualified Data.ByteString.Base16 as Base16@@ -50,23 +51,22 @@ import qualified Data.ByteString.Unsafe as BS import Data.Text (Text) import qualified Data.Text as Text+import qualified Data.Text.Builder.Linear as Builder+import Data.Text.Display import qualified Data.Text.Encoding as Text-import qualified Data.Text.Lazy.Builder as Builder import Foreign hiding (void)-import Foreign.C (CSize, CUChar, CULLong)+import Foreign.C (CChar, CSize, CUChar, CULLong) import GHC.Exception (Exception)-import GHC.IO.Handle.Text (memcpy)-import System.IO.Unsafe (unsafeDupablePerformIO)--import qualified Data.Base16.Types as Base16-import Data.Text.Display import LibSodium.Bindings.ShortHashing ( cryptoShortHashSipHashX24Bytes , cryptoShortHashSipHashX24KeyBytes , cryptoShortHashX24 , cryptoShortHashX24KeyGen )+import System.IO.Unsafe (unsafeDupablePerformIO)+ import Sel.Internal+import Sel.Internal.Sodium (binaryToHex) -- $introduction --@@ -89,16 +89,14 @@ -- @since 0.0.1.0 instance Eq ShortHash where (ShortHash sh1) == (ShortHash sh2) =- unsafeDupablePerformIO $- foreignPtrEq sh1 sh2 cryptoShortHashSipHashX24Bytes+ foreignPtrEq sh1 sh2 cryptoShortHashSipHashX24Bytes -- | -- -- @since 0.0.1.0 instance Ord ShortHash where compare (ShortHash sh1) (ShortHash sh2) =- unsafeDupablePerformIO $- foreignPtrOrd sh1 sh2 cryptoShortHashSipHashX24Bytes+ foreignPtrOrd sh1 sh2 cryptoShortHashSipHashX24Bytes -- | --@@ -124,8 +122,8 @@ -- ^ Random key produced by 'newKey' -> StrictByteString -- ^ Data to hash- -> IO ShortHash-hashByteString (ShortHashKey keyFPtr) message =+ -> ShortHash+hashByteString (ShortHashKey keyFPtr) message = unsafeDupablePerformIO $ BS.unsafeUseAsCStringLen message $ \(cString, cStringLen) -> do shortHashFPtr <- Foreign.mallocForeignPtrBytes (fromIntegral cryptoShortHashSipHashX24Bytes) Foreign.withForeignPtr keyFPtr $ \keyPtr ->@@ -151,7 +149,7 @@ -- ^ Random key produced by 'newKey' -> Text -- ^ UTF-8 encoded data to hash- -> IO ShortHash+ -> ShortHash hashText key message = hashByteString key (Text.encodeUtf8 message) -- | Convert a 'ShortHash' to a strict binary 'StrictByteString'.@@ -164,11 +162,12 @@ 0 (fromIntegral @CSize @Int cryptoShortHashSipHashX24Bytes) --- | Convert a 'ShortHash' to a hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'ShortHash' to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- @since 0.0.1.0 shortHashToHexByteString :: ShortHash -> StrictByteString-shortHashToHexByteString = Base16.extractBase16 . Base16.encodeBase16' . shortHashToBinary+shortHashToHexByteString (ShortHash hashForeignPtr) =+ binaryToHex hashForeignPtr cryptoShortHashSipHashX24Bytes -- | Convert a 'ShortHash' to a strict hexadecimal-encoded 'Text'. --@@ -188,16 +187,14 @@ -- @since 0.0.1.0 instance Eq ShortHashKey where (ShortHashKey sh1) == (ShortHashKey sh2) =- unsafeDupablePerformIO $- foreignPtrEq sh1 sh2 cryptoShortHashSipHashX24Bytes+ foreignPtrEq sh1 sh2 cryptoShortHashSipHashX24Bytes -- | -- -- @since 0.0.1.0 instance Ord ShortHashKey where compare (ShortHashKey sh1) (ShortHashKey sh2) =- unsafeDupablePerformIO $- foreignPtrOrd sh1 sh2 cryptoShortHashSipHashX24Bytes+ foreignPtrOrd sh1 sh2 cryptoShortHashSipHashX24Bytes -- | --@@ -229,11 +226,12 @@ 0 (fromIntegral @CSize @Int cryptoShortHashSipHashX24KeyBytes) --- | Convert a 'ShortHash' to a hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'ShortHashKey' to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- @since 0.0.1.0 shortHashKeyToHexByteString :: ShortHashKey -> StrictByteString-shortHashKeyToHexByteString = Base16.extractBase16 . Base16.encodeBase16' . shortHashKeyToBinary+shortHashKeyToHexByteString (ShortHashKey hashKeyForeignPtr) =+ binaryToHex hashKeyForeignPtr cryptoShortHashSipHashX24KeyBytes -- | Convert a 'ShortHash' to a strict hexadecimal-encoded 'Text'. --@@ -254,7 +252,10 @@ BS.unsafeUseAsCString binaryKey $ \cString -> do shortHashKeyFPtr <- Foreign.mallocForeignPtrBytes (fromIntegral cryptoShortHashSipHashX24KeyBytes) Foreign.withForeignPtr shortHashKeyFPtr $ \shortHashKeyPtr ->- memcpy shortHashKeyPtr (Foreign.castPtr cString) cryptoShortHashSipHashX24KeyBytes+ Foreign.copyBytes+ shortHashKeyPtr+ (Foreign.castPtr @CChar @CUChar cString)+ (fromIntegral cryptoShortHashSipHashX24KeyBytes) pure $ Just $ ShortHashKey shortHashKeyFPtr -- | Convert a strict hexadecimal-encoded 'Text' to a 'ShortHashKey'.@@ -282,11 +283,11 @@ -- @since 0.0.1.0 data ShortHashingException = ShortHashingException deriving stock- ( Show- -- ^ @since 0.0.1.0- , Eq+ ( Eq -- ^ @since 0.0.1.0 , Ord+ -- ^ @since 0.0.1.0+ , Show -- ^ @since 0.0.1.0 ) deriving anyclass
src/Sel/Internal.hs view
@@ -1,53 +1,115 @@ {-# LANGUAGE BangPatterns #-}-{-# LANGUAGE CApiFFI #-}+{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE KindSignatures #-}-{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE ViewPatterns #-} module Sel.Internal where +import Control.Monad (when) import Control.Monad.IO.Class (MonadIO, liftIO)-import qualified Data.Base16.Types as Base16-import qualified Data.ByteString.Base16 as Base16-import qualified Data.ByteString.Internal as BS+import Control.Monad.Trans.Class (lift)+import Data.Base16.Types qualified as Base16+import Data.ByteString (StrictByteString)+import Data.ByteString.Base16 qualified as Base16+import Data.ByteString.Internal (memcmp)+import Data.ByteString.Internal qualified as ByteString+import Data.Coerce (coerce) import Data.Kind (Type)-import Foreign (Ptr, castForeignPtr)-import Foreign.C.Types (CInt (CInt), CSize (CSize))-import Foreign.ForeignPtr (ForeignPtr, withForeignPtr)-import LibSodium.Bindings.SecureMemory (sodiumFree, sodiumMalloc)+import Foreign (ForeignPtr, Ptr)+import Foreign qualified+import Foreign.C (CSize, CUChar, throwErrno)+import Foreign.C.Types (CChar)+import LibSodium.Bindings.Comparison (sodiumCompare, sodiumMemcmp)+import LibSodium.Bindings.SecureMemory (finalizerSodiumFree, sodiumFree, sodiumMalloc)+import System.IO.Unsafe (unsafeDupablePerformIO) --- | This calls to C's @memcmp@ function, used in lieu of--- libsodium's @memcmp@ in cases when the return code is necessary.-foreign import capi unsafe "string.h memcmp"- memcmp :: Ptr a -> Ptr b -> CSize -> IO CInt+import Sel.Internal.Scoped+import Sel.Internal.Scoped.Foreign --- | Compare if the contents of two @ForeignPtr@s are equal.-foreignPtrEq :: ForeignPtr a -> ForeignPtr a -> CSize -> IO Bool-foreignPtrEq fptr1 fptr2 size =- withForeignPtr fptr1 $ \p ->- withForeignPtr fptr2 $ \q ->- do- result <- memcmp p q size- return $ 0 == result+-- | Compare the contents of two byte arrays for equality in constant time.+--+-- /See:/ [Constant-time test for equality](https://doc.libsodium.org/helpers#constant-time-test-for-equality)+--+-- @since 0.0.3.0+foreignPtrEqConstantTime :: ForeignPtr CUChar -> ForeignPtr CUChar -> CSize -> Bool+foreignPtrEqConstantTime p q size =+ unsafeDupablePerformIO . fmap (== 0) . use $+ sodiumMemcmp <$> foreignPtr p <*> foreignPtr q <*> pure size --- | Compare the contents of two @ForeignPtr@s using lexicographical ordering.-foreignPtrOrd :: ForeignPtr a -> ForeignPtr a -> CSize -> IO Ordering-foreignPtrOrd fptr1 fptr2 size =- withForeignPtr fptr1 $ \p ->- withForeignPtr fptr2 $ \q ->- do- result <- memcmp p q size- return $- if- | result == 0 -> EQ- | result < 0 -> LT- | otherwise -> GT+-- | Lexicographically compare the contents of two byte arrays.+--+-- ⚠️ Such comparisons are vulnerable to timing attacks, and should be+-- avoided for secret data.+--+-- @since 0.0.1.0+foreignPtrOrd :: ForeignPtr CUChar -> ForeignPtr CUChar -> CSize -> Ordering+foreignPtrOrd p q size =+ unsafeDupablePerformIO . fmap (`compare` 0) . useM $+ memcmp+ <$> foreignPtr (coerce p)+ <*> foreignPtr (coerce q)+ <*> pure (fromIntegral size) +-- | Lexicographically compare the contents of two byte arrays in constant time.+--+-- /See:/ [Comparing large numbers](https://libsodium.gitbook.io/doc/helpers#comparing-large-numbers)+--+-- @since 0.0.3.0+foreignPtrOrdConstantTime :: ForeignPtr CUChar -> ForeignPtr CUChar -> CSize -> Ordering+foreignPtrOrdConstantTime p q size =+ unsafeDupablePerformIO . fmap (`compare` 0) . useM $+ sodiumCompare <$> foreignPtr p <*> foreignPtr q <*> pure size++-- | Compare two byte arrays for lexicographic equality.+--+-- ⚠️ Such comparisons are vulnerable to timing attacks, and should be+-- avoided for secret data.+--+-- @since 0.0.1.0+foreignPtrEq :: ForeignPtr CUChar -> ForeignPtr CUChar -> CSize -> Bool+foreignPtrEq p q size = foreignPtrOrd p q size == EQ++-- | Convert a @'ForeignPtr' a@ to a 'ByteString' of the given length+-- and render the hexadecimal-encoded bytes as a 'String'.+--+-- @since 0.0.1.0 foreignPtrShow :: ForeignPtr a -> CSize -> String-foreignPtrShow fptr size =- BS.unpackChars . Base16.extractBase16 . Base16.encodeBase16' $- BS.fromForeignPtr (Foreign.castForeignPtr fptr) 0 (fromIntegral @CSize @Int size)+foreignPtrShow (Foreign.castForeignPtr -> cstring) size =+ ByteString.unpackChars . Base16.extractBase16 . Base16.encodeBase16' $+ ByteString.fromForeignPtr cstring 0 (fromIntegral @CSize @Int size)++-- | Copy a byte array to a @libsodium@ pointer.+--+-- The size of the array is not checked. The input may be truncated if+-- it is too long, or an unchecked exception may be thrown if it is+-- too short.+--+-- @since 0.0.3.0+unsafeCopyToSodiumPointer :: CSize -> StrictByteString -> IO (ForeignPtr CUChar)+unsafeCopyToSodiumPointer size s = use $ do+ str <- unsafeCString s+ lift $ sodiumPointer size $ \k ->+ Foreign.copyArray+ (Foreign.castPtr @CUChar @CChar k)+ str+ (fromIntegral @CSize @Int size)++-- | Allocate secure memory and populate it with the provided action.+--+-- Memory is allocated with 'LibSodium.Bindings.SecureMemory.sodiumMalloc' (see notes).+--+-- A finalizer frees the memory when the key goes out of scope.+--+-- @since 0.0.3.0+sodiumPointer :: CSize -> (Ptr CUChar -> IO ()) -> IO (ForeignPtr CUChar)+sodiumPointer size action = do+ ptr <- sodiumMalloc size+ when (ptr == Foreign.nullPtr) $ do+ throwErrno "sodium_malloc"+ action ptr+ Foreign.newForeignPtr finalizerSodiumFree ptr -- | Securely allocate an amount of memory with 'sodiumMalloc' and pass -- a pointer to the region to the provided action.
+ src/Sel/Internal/Scoped.hs view
@@ -0,0 +1,84 @@+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE StandaloneKindSignatures #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++-- |+-- Module : Sel.Internal.Scoped+-- Description : Continuation-passing utilities+-- Copyright : (c) Jack Henahan, 2024+-- License : BSD-3-Clause+-- Maintainer : The Haskell Cryptography Group+-- Portability : GHC only+--+-- This module implements a version of @Codensity@, modeling delimited+-- continuations. Useful for avoiding extreme rightward drift in+-- chains of @withForeignPtr@ and friends.+module Sel.Internal.Scoped where++import Control.Monad (ap, void)+import Control.Monad.IO.Class (MonadIO (liftIO))+import Control.Monad.Trans.Class (MonadTrans (lift))+import Data.Kind (Type)+import Data.Type.Equality (type (~~))+import GHC.Exts (RuntimeRep, TYPE)++-- | @since 0.0.3.0+type Scoped :: forall {k} {rep :: RuntimeRep}. (k -> TYPE rep) -> Type -> Type+newtype Scoped m a = Scoped {runScoped :: forall b. (a -> m b) -> m b}++-- | @since 0.0.3.0+instance Functor (Scoped f) where+ fmap f (Scoped m) = Scoped $ \k -> m (k . f)+ {-# INLINE fmap #-}++-- | @since 0.0.3.0+instance Applicative (Scoped f) where+ pure a = Scoped $ \k -> k a+ {-# INLINE pure #-}++ (<*>) = ap+ {-# INLINE (<*>) #-}++-- | @since 0.0.3.0+instance Monad (Scoped f) where+ Scoped m >>= f = Scoped $ \k ->+ m $ \a -> runScoped (f a) k+ {-# INLINE (>>=) #-}++-- | @since 0.0.3.0+instance (MonadIO m', m' ~~ m) => MonadIO (Scoped m) where+ liftIO = lift . liftIO+ {-# INLINE liftIO #-}++-- | @since 0.0.3.0+instance MonadTrans Scoped where+ lift m = Scoped (m >>=)+ {-# INLINE lift #-}++-- | @since 0.0.3.0+reset :: Monad m => Scoped m a -> Scoped m a+reset = lift . use++-- | @since 0.0.3.0+shift :: Applicative m => (forall b. (a -> m b) -> Scoped m b) -> Scoped m a+shift f = Scoped $ use . f++-- | @since 0.0.3.0+use :: Applicative m => Scoped m a -> m a+use (Scoped m) = m pure++-- | @since 0.0.3.0+useM :: Monad m => Scoped m (m a) -> m a+useM f = use $ f >>= lift++-- | @since 0.0.3.0+use_ :: Applicative m => Scoped m a -> m ()+use_ = void . use++-- | @since 0.0.3.0+useM_ :: Monad m => Scoped m (m a) -> m ()+useM_ = void . useM
+ src/Sel/Internal/Scoped/Foreign.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE ImportQualifiedPost #-}++-- |+-- Module : Sel.Internal.Scoped.Foreign+-- Description : Scoped wrappers around pointer manipulation+-- Copyright : (c) Jack Henahan, 2024+-- License : BSD-3-Clause+-- Maintainer : The Haskell Cryptography Group+-- Portability : GHC only+--+-- This module wraps some common points of contact with 'Ptr',+-- 'ForeignPtr', and friends up in 'Scoped' for the sake of not saying+-- 'lift' absolutely everywhere.+module Sel.Internal.Scoped.Foreign where++import Control.Monad.Trans.Class (lift)+import Data.ByteString (StrictByteString)+import Data.ByteString.Unsafe qualified as ByteString+import Foreign (ForeignPtr, Ptr, Storable)+import Foreign qualified+import Foreign.C (CString, CStringLen)++import Sel.Internal.Scoped++-- | @since 0.0.3.0+foreignPtr :: ForeignPtr a -> Scoped IO (Ptr a)+foreignPtr fptr = Scoped $ Foreign.withForeignPtr fptr++-- | @since 0.0.3.0+unsafeCStringLen :: StrictByteString -> Scoped IO CStringLen+unsafeCStringLen bs = Scoped $ ByteString.unsafeUseAsCStringLen bs++-- | @since 0.0.3.0+unsafeCString :: StrictByteString -> Scoped IO CString+unsafeCString bs = Scoped $ ByteString.unsafeUseAsCString bs++-- | @since 0.0.3.0+mallocBytes :: Int -> Scoped IO (Ptr a)+mallocBytes = lift . Foreign.mallocBytes++-- | @since 0.0.3.0+mallocForeignPtrBytes :: Int -> Scoped IO (ForeignPtr a)+mallocForeignPtrBytes len = lift $ Foreign.mallocForeignPtrBytes len++-- | @since 0.0.3.0+copyArray :: Storable a => Ptr a -> Ptr a -> Int -> Scoped IO ()+copyArray target source len = lift $ Foreign.copyArray target source len
+ src/Sel/Internal/Sodium.hs view
@@ -0,0 +1,26 @@+{-# LANGUAGE ImportQualifiedPost #-}++module Sel.Internal.Sodium where++import Control.Monad.Trans.Class (lift)+import Data.ByteString (StrictByteString)+import Data.ByteString qualified as ByteString+import Foreign (ForeignPtr)+import Foreign.C (CSize, CUChar)+import LibSodium.Bindings.Utils+import System.IO.Unsafe (unsafeDupablePerformIO)++import Sel.Internal.Scoped+import Sel.Internal.Scoped.Foreign++-- | Convert a byte array to a hexadecimal-encoded 'StrictByteString' in constant time.+--+-- /See:/ [@sodium_bin2hex@](https://libsodium.gitbook.io/doc/helpers#hexadecimal-encoding-decoding)+--+-- @since 0.0.3.0+binaryToHex :: ForeignPtr CUChar -> CSize -> StrictByteString+binaryToHex fPtr size = unsafeDupablePerformIO . use $ do+ let hexLength = size * 2 + 1+ hexPtr <- foreignPtr =<< mallocForeignPtrBytes (fromIntegral hexLength)+ ptr <- foreignPtr fPtr+ lift $ ByteString.packCString =<< sodiumBin2Hex hexPtr hexLength ptr size
src/Sel/PublicKey/Cipher.hs view
@@ -33,12 +33,12 @@ , nonceFromHexByteString , nonceToHexByteString - -- ** Cipher text- , CipherText (..)- , cipherTextFromHexByteString- , cipherTextToHexText- , cipherTextToHexByteString- , cipherTextToBinary+ -- ** Ciphertext+ , Ciphertext (..)+ , ciphertextFromHexByteString+ , ciphertextToHexText+ , ciphertextToHexByteString+ , ciphertextToBinary -- ** Encryption and Decryption , encrypt@@ -49,7 +49,9 @@ , EncryptionError (..) ) where +import Control.Exception import Control.Monad (when)+import qualified Data.Base16.Types as Base16 import Data.ByteString (StrictByteString) import qualified Data.ByteString as BS import qualified Data.ByteString.Base16 as Base16@@ -57,18 +59,13 @@ import qualified Data.ByteString.Unsafe as BS import Data.Text (Text) import qualified Data.Text as Text+import qualified Data.Text.Builder.Linear as Builder import Data.Text.Display (Display (displayBuilder), OpaqueInstance (..), ShowInstance (..))-import qualified Data.Text.Lazy.Builder as Builder import Data.Word (Word8) import Foreign (ForeignPtr, Ptr) import qualified Foreign import Foreign.C (CChar, CSize, CUChar, CULLong) import qualified Foreign.C as Foreign-import GHC.IO.Handle.Text (memcpy)-import System.IO.Unsafe (unsafeDupablePerformIO)--import Control.Exception-import qualified Data.Base16.Types as Base16 import LibSodium.Bindings.CryptoBox ( cryptoBoxEasy , cryptoBoxKeyPair@@ -80,7 +77,10 @@ ) import LibSodium.Bindings.Random (randombytesBuf) import LibSodium.Bindings.SecureMemory (finalizerSodiumFree, sodiumFree, sodiumMalloc)+import System.IO.Unsafe (unsafeDupablePerformIO)+ import Sel.Internal+import Sel.Internal.Sodium (binaryToHex) -- $introduction -- Public-key authenticated encryption allows a sender to encrypt a confidential message@@ -116,16 +116,14 @@ -- @since 0.0.1.0 instance Eq SecretKey where (SecretKey hk1) == (SecretKey hk2) =- unsafeDupablePerformIO $- foreignPtrEq hk1 hk2 cryptoBoxSecretKeyBytes+ foreignPtrEqConstantTime hk1 hk2 cryptoBoxSecretKeyBytes -- | -- -- @since 0.0.1.0 instance Ord SecretKey where compare (SecretKey hk1) (SecretKey hk2) =- unsafeDupablePerformIO $- foreignPtrOrd hk1 hk2 cryptoBoxSecretKeyBytes+ foreignPtrOrdConstantTime hk1 hk2 cryptoBoxSecretKeyBytes -- | > show secretKey == "[REDACTED]" --@@ -148,16 +146,14 @@ -- @since 0.0.1.0 instance Eq PublicKey where (PublicKey hk1) == (PublicKey hk2) =- unsafeDupablePerformIO $- foreignPtrEq hk1 hk2 cryptoBoxPublicKeyBytes+ foreignPtrEq hk1 hk2 cryptoBoxPublicKeyBytes -- | -- -- @since 0.0.1.0 instance Ord PublicKey where compare (PublicKey hk1) (PublicKey hk2) =- unsafeDupablePerformIO $- foreignPtrOrd hk1 hk2 cryptoBoxPublicKeyBytes+ foreignPtrOrd hk1 hk2 cryptoBoxPublicKeyBytes -- | --@@ -251,25 +247,14 @@ action publicKeyPtr secretKeyPtr pure (PublicKey publicKeyForeignPtr, SecretKey secretKeyForeignPtr) --- | Convert a 'SecretKey' to a hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'SecretKey' to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- ⚠️ Be prudent as to where you store it! -- -- @since 0.0.1.0 unsafeSecretKeyToHexByteString :: SecretKey -> StrictByteString unsafeSecretKeyToHexByteString (SecretKey secretKeyForeignPtr) =- Base16.extractBase16 . Base16.encodeBase16' $- BS.fromForeignPtr0- (Foreign.castForeignPtr @CUChar @Word8 secretKeyForeignPtr)- (fromIntegral @CSize @Int cryptoBoxSecretKeyBytes)-------- | Convert a 'SecretKey' to a hexadecimal-encoded 'StrictByteString'.------ ⚠️ Be prudent as to where you store it!------ @since 0.0.1.0+ binaryToHex secretKeyForeignPtr cryptoBoxSecretKeyBytes -- | A random number that must only be used once per exchanged message. -- It does not have to be confidential.@@ -288,16 +273,14 @@ -- @since 0.0.1.0 instance Eq Nonce where (Nonce hk1) == (Nonce hk2) =- unsafeDupablePerformIO $- foreignPtrEq hk1 hk2 cryptoBoxNonceBytes+ foreignPtrEq hk1 hk2 cryptoBoxNonceBytes -- | -- -- @since 0.0.1.0 instance Ord Nonce where compare (Nonce hk1) (Nonce hk2) =- unsafeDupablePerformIO $- foreignPtrOrd hk1 hk2 cryptoBoxNonceBytes+ foreignPtrOrd hk1 hk2 cryptoBoxNonceBytes -- | --@@ -337,113 +320,109 @@ pure $ Right $ Nonce (Foreign.castForeignPtr @CChar @CUChar nonceForeignPtr) Left msg -> pure $ Left msg --- | Convert a 'Nonce' to a hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'Nonce' to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- @since 0.0.1.0 nonceToHexByteString :: Nonce -> StrictByteString nonceToHexByteString (Nonce nonceForeignPtr) =- Base16.extractBase16 . Base16.encodeBase16' $- BS.fromForeignPtr0- (Foreign.castForeignPtr @CUChar @Word8 nonceForeignPtr)- (fromIntegral @CSize @Int cryptoBoxNonceBytes)+ binaryToHex nonceForeignPtr cryptoBoxNonceBytes -- | A ciphertext consisting of an encrypted message and an authentication tag. -- -- @since 0.0.1.0-data CipherText = CipherText+data Ciphertext = Ciphertext { messageLength :: CULLong- , cipherTextForeignPtr :: ForeignPtr CUChar+ , ciphertextForeignPtr :: ForeignPtr CUChar } -- | -- -- @since 0.0.1.0-instance Eq CipherText where- (CipherText messageLength1 hk1) == (CipherText messageLength2 hk2) =- unsafeDupablePerformIO $ do- let result1 = messageLength1 == messageLength2- result2 <-- foreignPtrEq- hk1- hk2- (fromIntegral messageLength1)- pure $ result1 && result2+instance Eq Ciphertext where+ (Ciphertext messageLength1 hk1) == (Ciphertext messageLength2 hk2) =+ let+ messageLength = messageLength1 == messageLength2+ content = foreignPtrEqConstantTime hk1 hk2 (fromIntegral messageLength1)+ in+ messageLength && content -- | -- -- @since 0.0.1.0-instance Ord CipherText where- compare (CipherText messageLength1 hk1) (CipherText messageLength2 hk2) =- unsafeDupablePerformIO $ do- let result1 = compare messageLength1 messageLength2- result2 <- foreignPtrOrd hk1 hk2 (fromIntegral messageLength1 + cryptoBoxMACBytes)- pure $ result1 <> result2+instance Ord Ciphertext where+ compare (Ciphertext messageLength1 hk1) (Ciphertext messageLength2 hk2) =+ let+ messageLength = compare messageLength1 messageLength2+ content = foreignPtrOrdConstantTime hk1 hk2 (fromIntegral messageLength1 + cryptoBoxMACBytes)+ in+ messageLength <> content -- | ⚠️ Be prudent as to what you do with it! -- -- @since 0.0.1.0-instance Display CipherText where- displayBuilder = Builder.fromText . cipherTextToHexText+instance Display Ciphertext where+ displayBuilder = Builder.fromText . ciphertextToHexText -- | ⚠️ Be prudent as to what you do with it! -- -- @since 0.0.1.0-instance Show CipherText where- show = BS.unpackChars . cipherTextToHexByteString+instance Show Ciphertext where+ show = BS.unpackChars . ciphertextToHexByteString --- | Create a 'CipherText' from a binary 'StrictByteString' that you have obtained on your own,--- usually from the network or disk. It must be a valid cipherText built from the concatenation+-- | Create a 'Ciphertext' from a binary 'StrictByteString' that you have obtained on your own,+-- usually from the network or disk. It must be a valid ciphertext built from the concatenation -- of the encrypted message and the authentication tag. -- -- The input cipher text, once decoded from base16, must be at least of length -- 'cryptoBoxMACBytes'. -- -- @since 0.0.1.0-cipherTextFromHexByteString :: StrictByteString -> Either Text CipherText-cipherTextFromHexByteString hexByteString = unsafeDupablePerformIO $+ciphertextFromHexByteString :: StrictByteString -> Either Text Ciphertext+ciphertextFromHexByteString hexByteString = unsafeDupablePerformIO $ case Base16.decodeBase16Untyped hexByteString of Right bytestring -> if BS.length bytestring >= fromIntegral cryptoBoxMACBytes- then BS.unsafeUseAsCStringLen bytestring $ \(outsideCipherTextPtr, outsideCipherTextLength) -> do- cipherTextForeignPtr <- BS.mallocByteString @CChar outsideCipherTextLength- Foreign.withForeignPtr cipherTextForeignPtr $ \cipherTextPtr ->- Foreign.copyArray cipherTextPtr outsideCipherTextPtr outsideCipherTextLength+ then BS.unsafeUseAsCStringLen bytestring $ \(outsideCiphertextPtr, outsideCiphertextLength) -> do+ ciphertextForeignPtr <- BS.mallocByteString @CChar outsideCiphertextLength+ Foreign.withForeignPtr ciphertextForeignPtr $ \ciphertextPtr ->+ Foreign.copyArray ciphertextPtr outsideCiphertextPtr outsideCiphertextLength pure $ Right $- CipherText- (fromIntegral @Int @CULLong outsideCipherTextLength - fromIntegral @CSize @CULLong cryptoBoxMACBytes)- (Foreign.castForeignPtr @CChar @CUChar cipherTextForeignPtr)+ Ciphertext+ (fromIntegral @Int @CULLong outsideCiphertextLength - fromIntegral @CSize @CULLong cryptoBoxMACBytes)+ (Foreign.castForeignPtr @CChar @CUChar ciphertextForeignPtr) else pure $ Left $ Text.pack "Cipher text is too short" Left msg -> error (Text.unpack msg) --- | Convert a 'CipherText' to a hexadecimal-encoded 'Text'.+-- | Convert a 'Ciphertext' to a hexadecimal-encoded 'Text'. -- -- ⚠️ Be prudent as to where you store it! -- -- @since 0.0.1.0-cipherTextToHexText :: CipherText -> Text-cipherTextToHexText = Base16.extractBase16 . Base16.encodeBase16 . cipherTextToBinary+ciphertextToHexText :: Ciphertext -> Text+ciphertextToHexText = Base16.extractBase16 . Base16.encodeBase16 . ciphertextToBinary --- | Convert a 'CipherText' to a hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'Ciphertext' to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- ⚠️ Be prudent as to where you store it! -- -- @since 0.0.1.0-cipherTextToHexByteString :: CipherText -> StrictByteString-cipherTextToHexByteString = Base16.extractBase16 . Base16.encodeBase16' . cipherTextToBinary+ciphertextToHexByteString :: Ciphertext -> StrictByteString+ciphertextToHexByteString (Ciphertext messageLength fPtr) =+ binaryToHex fPtr (cryptoBoxMACBytes + fromIntegral messageLength) --- | Convert a 'CipherText' to a binary 'StrictByteString'.+-- | Convert a 'Ciphertext' to a binary 'StrictByteString'. -- -- ⚠️ Be prudent as to where you store it! -- -- @since 0.0.1.0-cipherTextToBinary :: CipherText -> StrictByteString-cipherTextToBinary (CipherText messageLength fPtr) =+ciphertextToBinary :: Ciphertext -> StrictByteString+ciphertextToBinary (Ciphertext messageLength fPtr) = BS.fromForeignPtr0 (Foreign.castForeignPtr fPtr) (fromIntegral messageLength + fromIntegral cryptoBoxMACBytes) --- | Create an authenticated 'CipherText' from a message, a 'SecretKey',+-- | Create an authenticated 'Ciphertext' from a message, a 'SecretKey', -- and a one-time cryptographic 'Nonce' that must never be re-used with the same -- secret key to encrypt another message. --@@ -455,33 +434,33 @@ -- ^ Public key of the recipient -> SecretKey -- ^ Secret key of the sender- -> IO (Nonce, CipherText)+ -> IO (Nonce, Ciphertext) encrypt message (PublicKey publicKeyForeignPtr) (SecretKey secretKeyForeignPtr) = BS.unsafeUseAsCStringLen message $ \(cString, cStringLen) -> do (Nonce nonceForeignPtr) <- newNonce- cipherTextForeignPtr <- Foreign.mallocForeignPtrBytes (cStringLen + fromIntegral cryptoBoxMACBytes)- Foreign.withForeignPtr cipherTextForeignPtr $ \cipherTextPtr ->+ ciphertextForeignPtr <- Foreign.mallocForeignPtrBytes (cStringLen + fromIntegral cryptoBoxMACBytes)+ Foreign.withForeignPtr ciphertextForeignPtr $ \ciphertextPtr -> Foreign.withForeignPtr publicKeyForeignPtr $ \publicKeyPtr -> Foreign.withForeignPtr secretKeyForeignPtr $ \secretKeyPtr -> Foreign.withForeignPtr nonceForeignPtr $ \noncePtr -> do result <- cryptoBoxEasy- cipherTextPtr+ ciphertextPtr (Foreign.castPtr @CChar @CUChar cString) (fromIntegral @Int @CULLong cStringLen) noncePtr publicKeyPtr secretKeyPtr when (result /= 0) $ throw EncryptionError- let cipherText = CipherText (fromIntegral @Int @CULLong cStringLen) cipherTextForeignPtr- pure (Nonce nonceForeignPtr, cipherText)+ let ciphertext = Ciphertext (fromIntegral @Int @CULLong cStringLen) ciphertextForeignPtr+ pure (Nonce nonceForeignPtr, ciphertext) --- | Decrypt a 'CipherText' and authenticated message with the shared+-- | Decrypt a 'Ciphertext' and authenticated message with the shared -- secret key and the one-time cryptographic nonce. -- -- @since 0.0.1.0 decrypt- :: CipherText+ :: Ciphertext -- ^ Encrypted message you want to decrypt. -> PublicKey -- ^ Public key of the sender.@@ -491,19 +470,19 @@ -- ^ Nonce used for encrypting the original message. -> Maybe StrictByteString decrypt- CipherText{messageLength, cipherTextForeignPtr}+ Ciphertext{messageLength, ciphertextForeignPtr} (PublicKey publicKeyForeignPtr) (SecretKey secretKeyForeignPtr) (Nonce nonceForeignPtr) = unsafeDupablePerformIO $ do messagePtr <- Foreign.mallocBytes (fromIntegral @CULLong @Int messageLength)- Foreign.withForeignPtr cipherTextForeignPtr $ \cipherTextPtr ->+ Foreign.withForeignPtr ciphertextForeignPtr $ \ciphertextPtr -> Foreign.withForeignPtr publicKeyForeignPtr $ \publicKeyPtr -> Foreign.withForeignPtr secretKeyForeignPtr $ \secretKeyPtr -> Foreign.withForeignPtr nonceForeignPtr $ \noncePtr -> do result <- cryptoBoxOpenEasy messagePtr- cipherTextPtr+ ciphertextPtr (messageLength + fromIntegral @CSize @CULLong cryptoBoxMACBytes) noncePtr publicKeyPtr@@ -512,10 +491,10 @@ (-1) -> pure Nothing _ -> do bsPtr <- Foreign.mallocBytes (fromIntegral messageLength)- memcpy bsPtr (Foreign.castPtr messagePtr) (fromIntegral messageLength)+ Foreign.copyBytes bsPtr messagePtr (fromIntegral messageLength) Just <$> BS.unsafePackMallocCStringLen- (Foreign.castPtr @CChar bsPtr, fromIntegral messageLength)+ (Foreign.castPtr @CUChar @CChar bsPtr, fromIntegral messageLength) -- | Exception thrown upon error during the generation of -- the key pair by 'newKeyPair'.
src/Sel/PublicKey/Seal.hs view
@@ -38,11 +38,21 @@ import qualified Data.ByteString.Unsafe as BS import qualified Foreign import Foreign.C (CChar, CSize, CUChar, CULLong)-import GHC.IO.Handle.Text (memcpy)+import LibSodium.Bindings.SealedBoxes+ ( cryptoBoxSeal+ , cryptoBoxSealOpen+ , cryptoBoxSealbytes+ ) import System.IO.Unsafe (unsafeDupablePerformIO) -import LibSodium.Bindings.SealedBoxes (cryptoBoxSeal, cryptoBoxSealOpen, cryptoBoxSealbytes)-import Sel.PublicKey.Cipher (CipherText (CipherText), EncryptionError (..), KeyPairGenerationException, PublicKey (PublicKey), SecretKey (..), newKeyPair)+import Sel.PublicKey.Cipher+ ( Ciphertext (Ciphertext)+ , EncryptionError (..)+ , KeyPairGenerationException+ , PublicKey (PublicKey)+ , SecretKey (..)+ , newKeyPair+ ) -- $introduction -- Ephemeral authenticated encryption allows to anonymously send message to@@ -82,32 +92,32 @@ -- ^ Message to encrypt -> PublicKey -- ^ Public key of the recipient- -> IO CipherText+ -> IO Ciphertext seal messageByteString (PublicKey publicKeyFptr) = do BS.unsafeUseAsCStringLen messageByteString $ \(messagePtr, messageLen) -> do- cipherTextForeignPtr <-+ ciphertextForeignPtr <- Foreign.mallocForeignPtrBytes (messageLen + fromIntegral cryptoBoxSealbytes) Foreign.withForeignPtr publicKeyFptr $ \publicKeyPtr ->- Foreign.withForeignPtr cipherTextForeignPtr $ \cipherTextPtr -> do+ Foreign.withForeignPtr ciphertextForeignPtr $ \ciphertextPtr -> do result <- cryptoBoxSeal- cipherTextPtr+ ciphertextPtr (Foreign.castPtr @CChar @CUChar messagePtr) (fromIntegral @Int @CULLong messageLen) publicKeyPtr when (result /= 0) $ throw EncryptionError pure $- CipherText+ Ciphertext (fromIntegral @Int @CULLong messageLen)- cipherTextForeignPtr+ ciphertextForeignPtr -- | Open a sealed message from an unknown sender. -- You need your public and secret keys. -- -- @since 0.0.1.0 open- :: CipherText+ :: Ciphertext -- ^ Cipher to decrypt -> PublicKey -- ^ Public key of the recipient@@ -115,17 +125,17 @@ -- ^ Secret key of the recipient -> Maybe StrictByteString open- (CipherText messageLen cipherForeignPtr)+ (Ciphertext messageLen cipherForeignPtr) (PublicKey publicKeyFPtr) (SecretKey secretKeyFPtr) = unsafeDupablePerformIO $ do messagePtr <- Foreign.mallocBytes (fromIntegral @CULLong @Int messageLen)- Foreign.withForeignPtr cipherForeignPtr $ \cipherTextPtr ->+ Foreign.withForeignPtr cipherForeignPtr $ \ciphertextPtr -> Foreign.withForeignPtr publicKeyFPtr $ \publicKeyPtr -> Foreign.withForeignPtr secretKeyFPtr $ \secretKeyPtr -> do result <- cryptoBoxSealOpen messagePtr- cipherTextPtr+ ciphertextPtr (messageLen + fromIntegral @CSize @CULLong cryptoBoxSealbytes) publicKeyPtr secretKeyPtr@@ -133,7 +143,7 @@ (-1) -> pure Nothing _ -> do bsPtr <- Foreign.mallocBytes (fromIntegral messageLen)- memcpy bsPtr (Foreign.castPtr messagePtr) (fromIntegral messageLen)+ Foreign.copyBytes bsPtr messagePtr (fromIntegral messageLen) Just <$> BS.unsafePackMallocCStringLen- (Foreign.castPtr @CChar bsPtr, fromIntegral messageLen)+ (Foreign.castPtr @CUChar @CChar bsPtr, fromIntegral messageLen)
src/Sel/PublicKey/Signature.hs view
@@ -48,8 +48,6 @@ import qualified Foreign.Marshal.Array as Foreign import qualified Foreign.Ptr as Foreign import GHC.IO.Handle.Text (memcpy)-import System.IO.Unsafe (unsafeDupablePerformIO)- import LibSodium.Bindings.CryptoSign ( cryptoSignBytes , cryptoSignDetached@@ -58,6 +56,8 @@ , cryptoSignSecretKeyBytes , cryptoSignVerifyDetached )+import System.IO.Unsafe (unsafeDupablePerformIO)+ import Sel.Internal -- $introduction@@ -81,16 +81,14 @@ -- @since 0.0.1.0 instance Eq PublicKey where (PublicKey pk1) == (PublicKey pk2) =- unsafeDupablePerformIO $- foreignPtrEq pk1 pk2 cryptoSignPublicKeyBytes+ foreignPtrEq pk1 pk2 cryptoSignPublicKeyBytes -- | -- -- @since 0.0.1.0 instance Ord PublicKey where compare (PublicKey pk1) (PublicKey pk2) =- unsafeDupablePerformIO $- foreignPtrOrd pk1 pk2 cryptoSignPublicKeyBytes+ foreignPtrOrd pk1 pk2 cryptoSignPublicKeyBytes -- | --@@ -102,16 +100,14 @@ -- @since 0.0.1.0 instance Eq SecretKey where (SecretKey sk1) == (SecretKey sk2) =- unsafeDupablePerformIO $- foreignPtrEq sk1 sk2 cryptoSignSecretKeyBytes+ foreignPtrEqConstantTime sk1 sk2 cryptoSignSecretKeyBytes -- | -- -- @since 0.0.1.0 instance Ord SecretKey where compare (SecretKey sk1) (SecretKey sk2) =- unsafeDupablePerformIO $- foreignPtrOrd sk1 sk2 cryptoSignSecretKeyBytes+ foreignPtrOrd sk1 sk2 cryptoSignSecretKeyBytes -- | --@@ -127,20 +123,24 @@ -- @since 0.0.1.0 instance Eq SignedMessage where (SignedMessage len1 msg1 sig1) == (SignedMessage len2 msg2 sig2) =- unsafeDupablePerformIO $ do- result1 <- foreignPtrEq msg1 msg2 len1- result2 <- foreignPtrEq sig1 sig2 cryptoSignBytes- return $ (len1 == len2) && result1 && result2+ let+ messageLength = len1 == len2+ msg1Eq = foreignPtrEq msg1 msg2 len1+ msg2Eq = foreignPtrEq sig1 sig2 cryptoSignBytes+ in+ messageLength && msg1Eq && msg2Eq -- | -- -- @since 0.0.1.0 instance Ord SignedMessage where compare (SignedMessage len1 msg1 sig1) (SignedMessage len2 msg2 sig2) =- unsafeDupablePerformIO $ do- result1 <- foreignPtrOrd msg1 msg2 len1- result2 <- foreignPtrOrd sig1 sig2 cryptoSignBytes- return $ compare len1 len2 <> result1 <> result2+ let+ messageLength = compare len1 len2+ msg1Ord = foreignPtrOrd msg1 msg2 len1+ msg2Ord = foreignPtrOrd sig1 sig2 cryptoSignBytes+ in+ messageLength <> msg1Ord <> msg2Ord -- | Generate a pair of public and secret key. --@@ -161,6 +161,15 @@ pure (PublicKey publicKeyForeignPtr, SecretKey secretKeyForeignPtr) -- | Sign a message.+--+-- Note that, if @libsodium@ is compiled with the @ED25519_NONDETERMINISTIC@+-- macro defined, this function will produce non-deterministic (but also+-- non-standard) Ed25519 signatures. If @libsodium@ hasn't been compiled with+-- the @ED25519_NONDETERMINISTIC@ macro defined, it's safe to call this+-- function in a pure context with 'unsafeDupablePerformIO'.+--+-- For more information, see the+-- [@libsodium@ docs](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#notes). -- -- @since 0.0.1.0 signMessage :: StrictByteString -> SecretKey -> IO SignedMessage
src/Sel/Scrypt.hs view
@@ -31,14 +31,15 @@ import qualified Data.ByteString.Internal as BS import Data.ByteString.Unsafe (unsafeUseAsCStringLen) import Data.Text as Text+import qualified Data.Text.Builder.Linear as Builder import Data.Text.Display import qualified Data.Text.Encoding as Text-import qualified Data.Text.Lazy.Builder as Builder import Foreign hiding (void) import Foreign.C import LibSodium.Bindings.Scrypt+ import Sel.Internal-import System.IO.Unsafe (unsafeDupablePerformIO)+import Sel.Internal.Sodium (binaryToHex) -- $introduction --@@ -54,14 +55,18 @@ -- | @since 0.0.1.0 instance Eq ScryptHash where (ScryptHash sh1) == (ScryptHash sh2) =- unsafeDupablePerformIO $- foreignPtrEq sh1 sh2 cryptoPWHashScryptSalsa208SHA256StrBytes+ foreignPtrEq+ (Foreign.castForeignPtr @CChar @CUChar sh1)+ (Foreign.castForeignPtr @CChar @CUChar sh2)+ cryptoPWHashScryptSalsa208SHA256StrBytes -- | @since 0.0.1.0 instance Ord ScryptHash where compare (ScryptHash sh1) (ScryptHash sh2) =- unsafeDupablePerformIO $- foreignPtrOrd sh1 sh2 cryptoPWHashScryptSalsa208SHA256StrBytes+ foreignPtrOrd+ (Foreign.castForeignPtr @CChar @CUChar sh1)+ (Foreign.castForeignPtr @CChar @CUChar sh2)+ cryptoPWHashScryptSalsa208SHA256StrBytes -- | @since 0.0.1.0 instance Show ScryptHash where@@ -112,12 +117,12 @@ (fromIntegral cStringLen) return (result == 0) --- | Convert a 'ScryptHash' to a binary 'StrictByteString'.+-- | Convert a 'ScryptHash' to a binary 'StrictByteString' in constant time. -- -- @since 0.0.1.0 scryptHashToByteString :: ScryptHash -> StrictByteString scryptHashToByteString (ScryptHash fPtr) =- BS.fromForeignPtr0 (Foreign.castForeignPtr fPtr) (fromIntegral @CSize @Int cryptoPWHashScryptSalsa208SHA256StrBytes)+ binaryToHex (Foreign.castForeignPtr @CChar @CUChar fPtr) cryptoPWHashScryptSalsa208SHA256StrBytes -- | Convert a 'ScryptHash' to a hexadecimal-encoded 'Text'. --
src/Sel/SecretKey/Authentication.hs view
@@ -33,7 +33,6 @@ ) where import Control.Monad (void, when)-import qualified Data.Base16.Types as Base16 import Data.ByteString (StrictByteString) import qualified Data.ByteString as BS import qualified Data.ByteString.Base16 as Base16@@ -42,12 +41,9 @@ import Data.Text (Text) import qualified Data.Text as Text import Data.Text.Display (Display, OpaqueInstance (..), ShowInstance (..))-import Data.Word (Word8) import Foreign (ForeignPtr) import qualified Foreign-import Foreign.C (CChar, CSize, CUChar, CULLong, throwErrno)-import System.IO.Unsafe (unsafeDupablePerformIO)-+import Foreign.C (CChar, CUChar, CULLong, throwErrno) import LibSodium.Bindings.CryptoAuth ( cryptoAuth , cryptoAuthBytes@@ -56,7 +52,10 @@ , cryptoAuthVerify ) import LibSodium.Bindings.SecureMemory+import System.IO.Unsafe (unsafeDupablePerformIO)+ import Sel.Internal+import Sel.Internal.Sodium (binaryToHex) -- $introduction -- The 'authenticate' function computes an authentication tag for a message and a secret key,@@ -77,7 +76,7 @@ -- > authKey <- Auth.newAuthenticationKey -- > -- An authentication tag is computed for the message by the server -- > let message = "Hello, world!"--- > tag <- Auth.authenticate message+-- > tag = Auth.authenticate message -- > -- The server sends the message and its authentication tag -- > -- […] -- > -- The recipient of the message uses the shared secret to validate the message's tag@@ -92,9 +91,9 @@ -- ^ Message to authenticate -> AuthenticationKey -- ^ Secret key for authentication- -> IO AuthenticationTag+ -> AuthenticationTag -- ^ Cryptographic tag for authentication-authenticate message (AuthenticationKey authenticationKeyForeignPtr) =+authenticate message (AuthenticationKey authenticationKeyForeignPtr) = unsafeDupablePerformIO $ BS.unsafeUseAsCStringLen message $ \(cString, cStringLen) -> do authenticationTagForeignPtr <- Foreign.mallocForeignPtrBytes@@ -145,16 +144,14 @@ -- @since 0.0.1.0 instance Eq AuthenticationKey where (AuthenticationKey hk1) == (AuthenticationKey hk2) =- unsafeDupablePerformIO $- foreignPtrEq hk1 hk2 cryptoAuthKeyBytes+ foreignPtrEqConstantTime hk1 hk2 cryptoAuthKeyBytes -- | -- -- @since 0.0.1.0 instance Ord AuthenticationKey where compare (AuthenticationKey hk1) (AuthenticationKey hk2) =- unsafeDupablePerformIO $- foreignPtrOrd hk1 hk2 cryptoAuthKeyBytes+ foreignPtrOrdConstantTime hk1 hk2 cryptoAuthKeyBytes -- | > show authenticationKey == "[REDACTED]" --@@ -206,17 +203,14 @@ else pure $ Left $ Text.pack "Authentication Key is too short" Left msg -> pure $ Left msg --- | Convert a 'AuthenticationKey to a hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'AuthenticationKey to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- ⚠️ Be prudent as to where you store it! -- -- @since 0.0.1.0 unsafeAuthenticationKeyToHexByteString :: AuthenticationKey -> StrictByteString unsafeAuthenticationKeyToHexByteString (AuthenticationKey authenticationKeyForeignPtr) =- Base16.extractBase16 . Base16.encodeBase16' $- BS.fromForeignPtr0- (Foreign.castForeignPtr @CUChar @Word8 authenticationKeyForeignPtr)- (fromIntegral @CSize @Int cryptoAuthKeyBytes)+ binaryToHex authenticationKeyForeignPtr cryptoAuthKeyBytes -- | A secret authentication key of size 'cryptoAuthBytes'. --@@ -233,16 +227,14 @@ -- @since 0.0.1.0 instance Eq AuthenticationTag where (AuthenticationTag hk1) == (AuthenticationTag hk2) =- unsafeDupablePerformIO $- foreignPtrEq hk1 hk2 cryptoAuthBytes+ foreignPtrEqConstantTime hk1 hk2 cryptoAuthBytes -- | -- -- @since 0.0.1.0 instance Ord AuthenticationTag where compare (AuthenticationTag hk1) (AuthenticationTag hk2) =- unsafeDupablePerformIO $- foreignPtrOrd hk1 hk2 cryptoAuthBytes+ foreignPtrOrdConstantTime hk1 hk2 cryptoAuthBytes -- | --@@ -250,16 +242,12 @@ instance Show AuthenticationTag where show = BS.unpackChars . authenticationTagToHexByteString --- | Convert an 'AuthenticationTag' to a hexadecimal-encoded 'StrictByteString'.+-- | Convert an 'AuthenticationTag' to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- @since 0.0.1.0 authenticationTagToHexByteString :: AuthenticationTag -> StrictByteString authenticationTagToHexByteString (AuthenticationTag fPtr) =- Base16.extractBase16 $- Base16.encodeBase16' $- BS.fromForeignPtr0- (Foreign.castForeignPtr fPtr)- (fromIntegral cryptoAuthBytes)+ binaryToHex fPtr cryptoAuthBytes -- | Create an 'AuthenticationTag' from a binary 'StrictByteString' that you have obtained on your own, -- usually from the network or disk.
src/Sel/SecretKey/Cipher.hs view
@@ -34,12 +34,12 @@ , nonceFromHexByteString , nonceToHexByteString - -- ** Hash- , Hash- , hashFromHexByteString- , hashToBinary- , hashToHexByteString- , hashToHexText+ -- ** Ciphertext+ , Ciphertext+ , ciphertextFromHexByteString+ , ciphertextToBinary+ , ciphertextToHexByteString+ , ciphertextToHexText ) where import Control.Monad (void, when)@@ -51,15 +51,12 @@ import qualified Data.ByteString.Unsafe as BS import Data.Text (Text) import qualified Data.Text as Text+import qualified Data.Text.Builder.Linear as Builder import Data.Text.Display (Display (displayBuilder), OpaqueInstance (..), ShowInstance (..))-import qualified Data.Text.Lazy.Builder as Builder import Data.Word (Word8) import Foreign (ForeignPtr) import qualified Foreign import Foreign.C (CChar, CSize, CUChar, CULLong, throwErrno)-import GHC.IO.Handle.Text (memcpy)-import System.IO.Unsafe (unsafeDupablePerformIO)- import LibSodium.Bindings.Random (randombytesBuf) import LibSodium.Bindings.Secretbox ( cryptoSecretboxEasy@@ -70,12 +67,15 @@ , cryptoSecretboxOpenEasy ) import LibSodium.Bindings.SecureMemory+import System.IO.Unsafe (unsafeDupablePerformIO)+ import Sel.Internal+import Sel.Internal.Sodium (binaryToHex) -- $introduction -- "Authenticated Encryption" uses a secret key along with a single-use number -- called a "nonce" to encrypt a message.--- The resulting hash is accompanied by an authentication tag.+-- The resulting ciphertext is accompanied by an authentication tag. -- -- Encryption is done with the XSalsa20 stream cipher and authentication is done with the -- Poly1305 MAC hash.@@ -110,16 +110,14 @@ -- @since 0.0.1.0 instance Eq SecretKey where (SecretKey hk1) == (SecretKey hk2) =- unsafeDupablePerformIO $- foreignPtrEq hk1 hk2 cryptoSecretboxKeyBytes+ foreignPtrEqConstantTime hk1 hk2 cryptoSecretboxKeyBytes -- | -- -- @since 0.0.1.0 instance Ord SecretKey where compare (SecretKey hk1) (SecretKey hk2) =- unsafeDupablePerformIO $- foreignPtrOrd hk1 hk2 cryptoSecretboxKeyBytes+ foreignPtrOrdConstantTime hk1 hk2 cryptoSecretboxKeyBytes -- | > show secretKey == "[REDACTED]" --@@ -171,17 +169,14 @@ action ptr pure $ SecretKey fPtr --- | Convert a 'SecretKey' to a hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'SecretKey' to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- ⚠️ Be prudent as to where you store it! -- -- @since 0.0.1.0 unsafeSecretKeyToHexByteString :: SecretKey -> StrictByteString unsafeSecretKeyToHexByteString (SecretKey secretKeyForeignPtr) =- Base16.extractBase16 . Base16.encodeBase16' $- BS.fromForeignPtr0- (Foreign.castForeignPtr @CUChar @Word8 secretKeyForeignPtr)- (fromIntegral @CSize @Int cryptoSecretboxKeyBytes)+ binaryToHex secretKeyForeignPtr cryptoSecretboxKeyBytes -- | A random number that must only be used once per exchanged message. -- It does not have to be confidential.@@ -200,16 +195,14 @@ -- @since 0.0.1.0 instance Eq Nonce where (Nonce hk1) == (Nonce hk2) =- unsafeDupablePerformIO $- foreignPtrEq hk1 hk2 cryptoSecretboxNonceBytes+ foreignPtrEq hk1 hk2 cryptoSecretboxNonceBytes -- | -- -- @since 0.0.1.0 instance Ord Nonce where compare (Nonce hk1) (Nonce hk2) =- unsafeDupablePerformIO $- foreignPtrOrd hk1 hk2 cryptoSecretboxNonceBytes+ foreignPtrOrd hk1 hk2 cryptoSecretboxNonceBytes -- | --@@ -220,7 +213,7 @@ -- | Generate a new random nonce. -- Only use it once per exchanged message. ----- Do not use this outside of hash creation!+-- Do not use this outside of ciphertext creation! newNonce :: IO Nonce newNonce = do (fPtr :: ForeignPtr CUChar) <- Foreign.mallocForeignPtrBytes (fromIntegral cryptoSecretboxNonceBytes)@@ -252,111 +245,118 @@ else pure $ Left $ Text.pack "Nonce is too short" Left msg -> pure $ Left msg --- | Convert a 'Nonce' to a hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'Nonce' to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- @since 0.0.1.0 nonceToHexByteString :: Nonce -> StrictByteString nonceToHexByteString (Nonce nonceForeignPtr) =- Base16.extractBase16 . Base16.encodeBase16' $- BS.fromForeignPtr0- (Foreign.castForeignPtr @CUChar @Word8 nonceForeignPtr)- (fromIntegral @CSize @Int cryptoSecretboxNonceBytes)+ binaryToHex nonceForeignPtr cryptoSecretboxNonceBytes -- | A ciphertext consisting of an encrypted message and an authentication tag. -- -- @since 0.0.1.0-data Hash = Hash+data Ciphertext = Ciphertext { messageLength :: CULLong- , hashForeignPtr :: ForeignPtr CUChar+ , ciphertextForeignPtr :: ForeignPtr CUChar } -- | -- -- @since 0.0.1.0-instance Eq Hash where- (Hash messageLength1 hk1) == (Hash messageLength2 hk2) =- unsafeDupablePerformIO $ do- result1 <-- foreignPtrEq+instance Eq Ciphertext where+ (Ciphertext messageLength1 hk1) == (Ciphertext messageLength2 hk2) =+ let+ messageLength = messageLength1 == messageLength2+ content =+ foreignPtrEqConstantTime hk1 hk2 (fromIntegral messageLength1 + cryptoSecretboxMACBytes)- pure $ (messageLength1 == messageLength2) && result1+ in+ messageLength && content -- | -- -- @since 0.0.1.0-instance Ord Hash where- compare (Hash messageLength1 hk1) (Hash messageLength2 hk2) =- unsafeDupablePerformIO $ do- result1 <- foreignPtrOrd hk1 hk2 (fromIntegral messageLength1 + cryptoSecretboxMACBytes)- pure $ compare messageLength1 messageLength2 <> result1+instance Ord Ciphertext where+ compare (Ciphertext messageLength1 hk1) (Ciphertext messageLength2 hk2) =+ let+ messageLength = compare messageLength1 messageLength2+ content =+ foreignPtrOrdConstantTime+ hk1+ hk2+ (fromIntegral messageLength1 + cryptoSecretboxMACBytes)+ in+ messageLength <> content -- | ⚠️ Be prudent as to what you do with it! -- -- @since 0.0.1.0-instance Display Hash where- displayBuilder = Builder.fromText . hashToHexText+instance Display Ciphertext where+ displayBuilder = Builder.fromText . ciphertextToHexText -- | ⚠️ Be prudent as to what you do with it! -- -- @since 0.0.1.0-instance Show Hash where- show = BS.unpackChars . hashToHexByteString+instance Show Ciphertext where+ show = BS.unpackChars . ciphertextToHexByteString --- | Create a 'Hash' from a binary 'StrictByteString' that you have obtained on your own,--- usually from the network or disk. It must be a valid hash built from the concatenation--- of the encrypted message and the authentication tag.+-- | Create a 'Ciphertext' from a hexadecimal-encoded 'StrictByteString' that+-- you have obtained on your own, usually from the network or disk. It must be+-- a valid ciphertext built from the concatenation of the encrypted message and+-- the authentication tag. ----- The input hash must at least of length 'cryptoSecretboxMACBytes'+-- The input ciphertext must at least of length 'cryptoSecretboxMACBytes'. -- -- @since 0.0.1.0-hashFromHexByteString :: StrictByteString -> Either Text Hash-hashFromHexByteString hexHash = unsafeDupablePerformIO $- case Base16.decodeBase16Untyped hexHash of+ciphertextFromHexByteString :: StrictByteString -> Either Text Ciphertext+ciphertextFromHexByteString hexCiphertext = unsafeDupablePerformIO $+ case Base16.decodeBase16Untyped hexCiphertext of Right bytestring -> if BS.length bytestring >= fromIntegral cryptoSecretboxMACBytes- then BS.unsafeUseAsCStringLen bytestring $ \(outsideHashPtr, outsideHashLength) -> do- hashForeignPtr <- BS.mallocByteString @CChar outsideHashLength -- The foreign pointer that will receive the hash data.- Foreign.withForeignPtr hashForeignPtr $ \hashPtr ->- -- We copy bytes from 'outsideHashPtr' to 'hashPtr'.- Foreign.copyArray hashPtr outsideHashPtr outsideHashLength+ then BS.unsafeUseAsCStringLen bytestring $ \(outsideCiphertextPtr, outsideCiphertextLength) -> do+ ciphertextForeignPtr <- BS.mallocByteString @CChar outsideCiphertextLength -- The foreign pointer that will receive the ciphertext data.+ Foreign.withForeignPtr ciphertextForeignPtr $ \ciphertextPtr ->+ -- We copy bytes from 'outsideCiphertextPtr' to 'ciphertextPtr'.+ Foreign.copyArray ciphertextPtr outsideCiphertextPtr outsideCiphertextLength pure $ Right $- Hash- (fromIntegral @Int @CULLong outsideHashLength - fromIntegral @CSize @CULLong cryptoSecretboxMACBytes)- (Foreign.castForeignPtr @CChar @CUChar hashForeignPtr)- else pure $ Left $ Text.pack "Hash is too short"+ Ciphertext+ (fromIntegral @Int @CULLong outsideCiphertextLength - fromIntegral @CSize @CULLong cryptoSecretboxMACBytes)+ (Foreign.castForeignPtr @CChar @CUChar ciphertextForeignPtr)+ else pure $ Left $ Text.pack "Ciphertext is too short" Left msg -> pure $ Left msg --- | Convert a 'Hash' to a hexadecimal-encoded 'Text'.+-- | Convert a 'Ciphertext' to a hexadecimal-encoded 'Text'. -- -- ⚠️ Be prudent as to where you store it! -- -- @since 0.0.1.0-hashToHexText :: Hash -> Text-hashToHexText = Base16.extractBase16 . Base16.encodeBase16 . hashToBinary+ciphertextToHexText :: Ciphertext -> Text+ciphertextToHexText = Base16.extractBase16 . Base16.encodeBase16 . ciphertextToBinary --- | Convert a 'Hash' to a hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'Ciphertext' to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- ⚠️ Be prudent as to where you store it! -- -- @since 0.0.1.0-hashToHexByteString :: Hash -> StrictByteString-hashToHexByteString = Base16.extractBase16 . Base16.encodeBase16' . hashToBinary+ciphertextToHexByteString :: Ciphertext -> StrictByteString+ciphertextToHexByteString (Ciphertext messageLength fPtr) =+ binaryToHex fPtr (cryptoSecretboxMACBytes + fromIntegral messageLength) --- | Convert a 'Hash' to a binary 'StrictByteString'.+-- | Convert a 'Ciphertext' to a binary 'StrictByteString'. -- -- ⚠️ Be prudent as to where you store it! -- -- @since 0.0.1.0-hashToBinary :: Hash -> StrictByteString-hashToBinary (Hash messageLength fPtr) =+ciphertextToBinary :: Ciphertext -> StrictByteString+ciphertextToBinary (Ciphertext messageLength fPtr) = BS.fromForeignPtr0 (Foreign.castForeignPtr fPtr) (fromIntegral messageLength + fromIntegral cryptoSecretboxMACBytes) --- | Create an authenticated hash from a message, a secret key,+-- | Create an authenticated ciphertext from a message, a secret key, -- and a one-time cryptographic nonce that must never be re-used with the same -- secret key to encrypt another message. --@@ -366,46 +366,46 @@ -- ^ Message to encrypt. -> SecretKey -- ^ Secret key generated with 'newSecretKey'.- -> IO (Nonce, Hash)+ -> IO (Nonce, Ciphertext) encrypt message (SecretKey secretKeyForeignPtr) = BS.unsafeUseAsCStringLen message $ \(cString, cStringLen) -> do (Nonce nonceForeignPtr) <- newNonce- hashForeignPtr <-+ ciphertextForeignPtr <- Foreign.mallocForeignPtrBytes (cStringLen + fromIntegral cryptoSecretboxMACBytes)- Foreign.withForeignPtr hashForeignPtr $ \hashPtr ->+ Foreign.withForeignPtr ciphertextForeignPtr $ \ciphertextPtr -> Foreign.withForeignPtr secretKeyForeignPtr $ \secretKeyPtr -> Foreign.withForeignPtr nonceForeignPtr $ \noncePtr -> do void $ cryptoSecretboxEasy- hashPtr+ ciphertextPtr (Foreign.castPtr @CChar @CUChar cString) (fromIntegral @Int @CULLong cStringLen) noncePtr secretKeyPtr- let hash = Hash (fromIntegral @Int @CULLong cStringLen) hashForeignPtr- pure (Nonce nonceForeignPtr, hash)+ let ciphertext = Ciphertext (fromIntegral @Int @CULLong cStringLen) ciphertextForeignPtr+ pure (Nonce nonceForeignPtr, ciphertext) --- | Decrypt a hashed and authenticated message with the shared secret key and the one-time cryptographic nonce.+-- | Decrypt an encrypted and authenticated message with the shared secret key and the one-time cryptographic nonce. -- -- @since 0.0.1.0 decrypt- :: Hash+ :: Ciphertext -- ^ Encrypted message you want to decrypt. -> SecretKey -- ^ Secret key used for encrypting the original message. -> Nonce -- ^ Nonce used for encrypting the original message. -> Maybe StrictByteString-decrypt Hash{messageLength, hashForeignPtr} (SecretKey secretKeyForeignPtr) (Nonce nonceForeignPtr) = unsafeDupablePerformIO $ do+decrypt Ciphertext{messageLength, ciphertextForeignPtr} (SecretKey secretKeyForeignPtr) (Nonce nonceForeignPtr) = unsafeDupablePerformIO $ do messagePtr <- Foreign.mallocBytes (fromIntegral @CULLong @Int messageLength)- Foreign.withForeignPtr hashForeignPtr $ \hashPtr ->+ Foreign.withForeignPtr ciphertextForeignPtr $ \ciphertextPtr -> Foreign.withForeignPtr secretKeyForeignPtr $ \secretKeyPtr -> Foreign.withForeignPtr nonceForeignPtr $ \noncePtr -> do result <- cryptoSecretboxOpenEasy messagePtr- hashPtr+ ciphertextPtr (messageLength + fromIntegral cryptoSecretboxMACBytes) noncePtr secretKeyPtr@@ -413,7 +413,7 @@ (-1) -> pure Nothing _ -> do bsPtr <- Foreign.mallocBytes (fromIntegral messageLength)- memcpy bsPtr (Foreign.castPtr messagePtr) (fromIntegral messageLength)+ Foreign.copyBytes bsPtr messagePtr (fromIntegral messageLength) Just <$> BS.unsafePackMallocCStringLen- (Foreign.castPtr @CChar bsPtr, fromIntegral messageLength)+ (Foreign.castPtr @CUChar @CChar bsPtr, fromIntegral messageLength)
src/Sel/SecretKey/Stream.hs view
@@ -12,7 +12,7 @@ -- | -- -- Module: Sel.SecretKey.Stream--- Description: Encrypted Streams with ChaCha20Poly1305+-- Description: Encrypted Streams with XChaCha20-Poly1305 -- Copyright: (C) Hécate Moonlight 2024 -- License: BSD-3-Clause -- Maintainer: The Haskell Cryptography Group@@ -51,8 +51,16 @@ -- ** Message Tags , MessageTag (..) - -- ** CipherText- , CipherText+ -- ** Additional data (AD)+ , AdditionalData (..)+ , AdditionalDataHexDecodingError (..)+ , additionalDataFromHexByteString+ , additionalDataToBinary+ , additionalDataToHexByteString+ , additionalDataToHexText++ -- ** Ciphertext+ , Ciphertext , ciphertextFromHexByteString , ciphertextToBinary , ciphertextToHexByteString@@ -78,15 +86,12 @@ import qualified Data.List as List import Data.Text (Text) import qualified Data.Text as Text-import Data.Text.Display (Display (..), OpaqueInstance (..))-import qualified Data.Text.Lazy.Builder as Builder-import Data.Word (Word8)+import qualified Data.Text.Builder.Linear as Builder+import Data.Text.Display (Display (..), OpaqueInstance (..), ShowInstance (..)) import Foreign (ForeignPtr, Ptr) import qualified Foreign import Foreign.C (CChar, CSize, CUChar, CULLong) import Foreign.C.Error (throwErrno)-import System.IO.Unsafe (unsafeDupablePerformIO)- import LibSodium.Bindings.SecretStream ( CryptoSecretStreamXChaCha20Poly1305State , cryptoSecretStreamXChaCha20Poly1305ABytes@@ -104,7 +109,10 @@ , cryptoSecretStreamXChaCha20Poly1305TagRekey ) import LibSodium.Bindings.SecureMemory (finalizerSodiumFree, sodiumMalloc)+import System.IO.Unsafe (unsafeDupablePerformIO)+ import Sel.Internal (allocateWith, foreignPtrEq, foreignPtrOrd)+import Sel.Internal.Sodium (binaryToHex) -- $introduction -- This high-level API encrypts a sequence of messages, or a single message split into an arbitrary number of chunks, using a secret key, with the following properties:@@ -121,15 +129,15 @@ -- $usage -- -- >>> secretKey <- Stream.newSecretKey--- >>> (header, cipherTexts) <- Stream.encryptStream secretKey $ \multipartState -> do -- we are in MonadIO+-- >>> (header, ciphertexts) <- Stream.encryptStream secretKey $ \multipartState -> do -- we are in MonadIO -- ... message1 <- getMessage -- This is your way to fetch a message from outside -- ... encryptedChunk1 <- Stream.encryptChunk multipartState Stream.messag message1 -- ... message2 <- getMessage -- ... encryptedChunk2 <- Stream.encryptChunk multipartState Stream.Final message2 -- ... pure [encryptedChunk1, encryptedChunk2] -- >>> result <- Stream.decryptStream secretKey header $ \multipartState-> do--- ... forM encryptedMessages $ \cipherText -> do--- ... decryptChunk multipartState cipherText+-- ... forM encryptedMessages $ \ciphertext -> do+-- ... decryptChunk multipartState ciphertext -- | 'Multipart' is the cryptographic context for stream encryption. --@@ -138,7 +146,7 @@ type role Multipart nominal --- | Perform streaming hashing with a 'Multipart' cryptographic context.+-- | Perform streaming encryption with a 'Multipart' cryptographic context. -- -- Use 'Stream.encryptChunk' within the continuation. --@@ -184,41 +192,47 @@ -- ^ Cryptographic context -> MessageTag -- ^ Tag that will be associated with the message. See the documentation of 'MessageTag' to know which to choose when.+ -> Maybe AdditionalData+ -- ^ Additional data (AD) to be authenticated. -> StrictByteString -- ^ Message to encrypt.- -> m CipherText-encryptChunk (Multipart statePtr) messageTag message = liftIO $ BSU.unsafeUseAsCStringLen message $ \(cString, cStringLen) -> do- let messagePtr = Foreign.castPtr @CChar @CUChar cString- let messageLen = fromIntegral @Int @CULLong cStringLen- cipherTextFPtr <- Foreign.mallocForeignPtrBytes (cStringLen + fromIntegral cryptoSecretStreamXChaCha20Poly1305ABytes)- Foreign.withForeignPtr cipherTextFPtr $ \cipherTextBuffer -> do- result <-- cryptoSecretStreamXChaCha20Poly1305Push- statePtr- cipherTextBuffer- Foreign.nullPtr -- default size of messageLen + 'cryptoSecretStreamXChaCha20Poly1305ABytes'- messagePtr- messageLen- Foreign.nullPtr -- No additional data- 0 -- No additional data size- (messageTagToConstant messageTag)- when (result /= 0) $ throw StreamEncryptionException- pure $ CipherText (fromIntegral cStringLen) cipherTextFPtr+ -> m Ciphertext+encryptChunk (Multipart statePtr) messageTag mbAd message = liftIO $+ BSU.unsafeUseAsCStringLen message $ \(messageCString, messageCStringLen) ->+ BSU.unsafeUseAsCStringLen (maybe BS.empty additionalDataToBinary mbAd) $ \(adCString, adCStringLen) -> do+ let messagePtr = Foreign.castPtr @CChar @CUChar messageCString+ messageLen = fromIntegral @Int @CULLong messageCStringLen+ adPtr = Foreign.castPtr @CChar @CUChar adCString+ adLen = fromIntegral @Int @CULLong adCStringLen+ ciphertextFPtr <- Foreign.mallocForeignPtrBytes (messageCStringLen + fromIntegral cryptoSecretStreamXChaCha20Poly1305ABytes)+ Foreign.withForeignPtr ciphertextFPtr $ \ciphertextBuffer -> do+ result <-+ cryptoSecretStreamXChaCha20Poly1305Push+ statePtr+ ciphertextBuffer+ Foreign.nullPtr -- default size of messageLen + 'cryptoSecretStreamXChaCha20Poly1305ABytes'+ messagePtr+ messageLen+ adPtr+ adLen+ (messageTagToConstant messageTag)+ when (result /= 0) $ throw StreamEncryptionException+ pure $ Ciphertext (fromIntegral messageCStringLen) ciphertextFPtr -- | Perform streaming encryption of a finite list. -- -- This function can throw 'StreamEncryptionException' upon an error in the underlying implementation. -- -- @since 0.0.1.0-encryptList :: forall m. MonadIO m => SecretKey -> [StrictByteString] -> m (Header, [CipherText])+encryptList :: forall m. MonadIO m => SecretKey -> [(Maybe AdditionalData, StrictByteString)] -> m (Header, [Ciphertext]) encryptList secretKey messages = encryptStream secretKey $ \multipart -> go multipart messages [] where- go :: Multipart s -> [StrictByteString] -> [CipherText] -> m [CipherText]- go multipart [lastMsg] acc = do- encryptedChunk <- encryptChunk multipart Final lastMsg+ go :: Multipart s -> [(Maybe AdditionalData, StrictByteString)] -> [Ciphertext] -> m [Ciphertext]+ go multipart [(mbLastAd, lastMsg)] acc = do+ encryptedChunk <- encryptChunk multipart Final mbLastAd lastMsg pure $ List.reverse $ encryptedChunk : acc- go multipart (msg : rest) acc = do- encryptedChunk <- encryptChunk multipart Message msg+ go multipart ((mbAd, msg) : rest) acc = do+ encryptedChunk <- encryptChunk multipart Message mbAd msg go multipart rest (encryptedChunk : acc) go _ [] acc = pure acc @@ -264,41 +278,46 @@ . MonadIO m => Multipart s -- ^ Cryptographic context- -> CipherText+ -> Maybe AdditionalData+ -- ^ Additional data (AD) to be authenticated.+ -> Ciphertext -- ^ Encrypted message portion to decrypt -> m StrictByteString -- ^ Decrypted message portion-decryptChunk (Multipart statePtr) CipherText{messageLength, cipherTextForeignPtr} = do+decryptChunk (Multipart statePtr) mbAd Ciphertext{messageLength, ciphertextForeignPtr} = do clearTextForeignPtr <- liftIO $ Foreign.mallocForeignPtrBytes (fromIntegral messageLength)- let cipherTextLen = messageLength + fromIntegral cryptoSecretStreamXChaCha20Poly1305ABytes- liftIO $ Foreign.withForeignPtr cipherTextForeignPtr $ \cipherTextBuffer -> do- liftIO $ Foreign.withForeignPtr clearTextForeignPtr $ \clearTextBuffer -> do- tagBuffer <- sodiumMalloc 1- result <-- cryptoSecretStreamXChaCha20Poly1305Pull- statePtr- clearTextBuffer- Foreign.nullPtr- tagBuffer- cipherTextBuffer- cipherTextLen- Foreign.nullPtr- 0- when (result /= 0) $ throw StreamDecryptionException- bsPtr <- Foreign.mallocBytes (fromIntegral messageLength)- Foreign.copyBytes bsPtr (Foreign.castPtr clearTextBuffer) (fromIntegral messageLength)- BSU.unsafePackMallocCStringLen (bsPtr, fromIntegral messageLength)+ let ciphertextLen = messageLength + fromIntegral cryptoSecretStreamXChaCha20Poly1305ABytes+ liftIO $ Foreign.withForeignPtr ciphertextForeignPtr $ \ciphertextBuffer -> do+ BSU.unsafeUseAsCStringLen (maybe BS.empty additionalDataToBinary mbAd) $ \(adCString, adCStringLen) -> do+ let adPtr = Foreign.castPtr @CChar @CUChar adCString+ adLen = fromIntegral @Int @CULLong adCStringLen+ liftIO $ Foreign.withForeignPtr clearTextForeignPtr $ \clearTextBuffer -> do+ tagBuffer <- sodiumMalloc 1+ result <-+ cryptoSecretStreamXChaCha20Poly1305Pull+ statePtr+ clearTextBuffer+ Foreign.nullPtr+ tagBuffer+ ciphertextBuffer+ ciphertextLen+ adPtr+ adLen+ when (result /= 0) $ throw StreamDecryptionException+ bsPtr <- Foreign.mallocBytes (fromIntegral messageLength)+ Foreign.copyBytes bsPtr (Foreign.castPtr clearTextBuffer) (fromIntegral messageLength)+ BSU.unsafePackMallocCStringLen (bsPtr, fromIntegral messageLength) -- | Perform streaming decryption of a finite Linked List. -- -- This function can throw 'StreamDecryptionException' if the chunk is invalid, incomplete, or corrupted. -- -- @since 0.0.1.0-decryptList :: forall m. MonadIO m => SecretKey -> Header -> [CipherText] -> m (Maybe [StrictByteString])+decryptList :: forall m. MonadIO m => SecretKey -> Header -> [(Maybe AdditionalData, Ciphertext)] -> m (Maybe [StrictByteString]) decryptList secretKey header encryptedMessages = decryptStream secretKey header $ \multipart -> do- forM encryptedMessages $ \cipherText -> do- decryptChunk multipart cipherText+ forM encryptedMessages $ \(mbAd, ciphertext) -> do+ decryptChunk multipart mbAd ciphertext -- | A secret key of size 'cryptoSecretStreamXChaCha20Poly1305KeyBytes'. --@@ -314,14 +333,12 @@ -- | @since 0.0.1.0 instance Eq SecretKey where (SecretKey hk1) == (SecretKey hk2) =- unsafeDupablePerformIO $- foreignPtrEq hk1 hk2 cryptoSecretStreamXChaCha20Poly1305KeyBytes+ foreignPtrEq hk1 hk2 cryptoSecretStreamXChaCha20Poly1305KeyBytes -- | @since 0.0.1.0 instance Ord SecretKey where compare (SecretKey hk1) (SecretKey hk2) =- unsafeDupablePerformIO $- foreignPtrOrd hk1 hk2 cryptoSecretStreamXChaCha20Poly1305KeyBytes+ foreignPtrOrd hk1 hk2 cryptoSecretStreamXChaCha20Poly1305KeyBytes -- | > show secretKey == "[REDACTED]" --@@ -357,17 +374,14 @@ else pure $ Left $ Text.pack ("Secret Key is not of size " <> show cryptoSecretStreamXChaCha20Poly1305KeyBytes) Left msg -> pure $ Left msg --- | Convert a 'SecretKey' to a hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'SecretKey' to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- ⚠️ Be prudent as to where you store it! -- -- @since 0.0.1.0 unsafeSecretKeyToHexByteString :: SecretKey -> Base16 StrictByteString unsafeSecretKeyToHexByteString (SecretKey secretKeyForeignPtr) =- Base16.encodeBase16' $- BSI.fromForeignPtr0- (Foreign.castForeignPtr @CUChar @Word8 secretKeyForeignPtr)- (fromIntegral @CSize @Int cryptoSecretStreamXChaCha20Poly1305KeyBytes)+ Base16.assertBase16 $ binaryToHex secretKeyForeignPtr cryptoSecretStreamXChaCha20Poly1305KeyBytes -- Prepare memory for a 'SecretKey' and use the provided action to fill it. --@@ -403,24 +417,19 @@ -- | @since 0.0.1.0 instance Eq Header where (Header header1) == (Header header2) =- unsafeDupablePerformIO $- foreignPtrEq header1 header2 cryptoSecretStreamXChaCha20Poly1305HeaderBytes+ foreignPtrEq header1 header2 cryptoSecretStreamXChaCha20Poly1305HeaderBytes -- | @since 0.0.1.0 instance Ord Header where compare (Header header1) (Header header2) =- unsafeDupablePerformIO $- foreignPtrOrd header1 header2 cryptoSecretStreamXChaCha20Poly1305HeaderBytes+ foreignPtrOrd header1 header2 cryptoSecretStreamXChaCha20Poly1305HeaderBytes --- | Convert a 'Header' to a hexadecimal-encoded 'StrictByteString'+-- | Convert a 'Header' to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- @since 0.0.1.0 headerToHexByteString :: Header -> Base16 StrictByteString headerToHexByteString (Header headerForeignPtr) =- Base16.encodeBase16' $- BSI.fromForeignPtr0- (Foreign.castForeignPtr @CUChar @Word8 headerForeignPtr)- (fromIntegral @CSize @Int cryptoSecretStreamXChaCha20Poly1305HeaderBytes)+ Base16.assertBase16 $ binaryToHex headerForeignPtr cryptoSecretStreamXChaCha20Poly1305HeaderBytes -- | Build a 'Header' from a base16-encoded 'StrictByteString' --@@ -471,94 +480,144 @@ Push -> fromIntegral cryptoSecretStreamXChaCha20Poly1305TagPush Rekey -> fromIntegral cryptoSecretStreamXChaCha20Poly1305TagRekey +-- | Additional data (AD). Also known as \"additional authenticated data\"+-- (AAD).+--+-- This refers to non-confidential data which is authenticated along with the+-- message, but not encrypted (i.e. its integrity is protected, but it is not+-- made confidential).+--+-- A typical use case for additional data is to authenticate protocol-specific+-- metadata about a message, such as its length and encoding.+newtype AdditionalData = AdditionalData StrictByteString+ deriving stock (Eq, Show)+ deriving (Display) via (ShowInstance AdditionalData)++-- | Convert an 'AdditionalData' value to hexadecimal-encoded 'Text'.+additionalDataToHexText :: AdditionalData -> Base16 Text+additionalDataToHexText = Base16.encodeBase16 . additionalDataToBinary++-- | Convert an 'AdditionalData' value to a hexadecimal-encoded+-- 'StrictByteString'.+additionalDataToHexByteString :: AdditionalData -> Base16 StrictByteString+additionalDataToHexByteString = Base16.encodeBase16' . additionalDataToBinary++-- | Convert an 'AdditionalData' value to a raw binary 'StrictByteString'.+additionalDataToBinary :: AdditionalData -> StrictByteString+additionalDataToBinary (AdditionalData bs) = bs++-- | Error decoding 'AdditionalData' from hexadecimal-encoded bytes.+newtype AdditionalDataHexDecodingError = AdditionalDataHexDecodingError Text+ deriving stock (Eq, Show)++-- | Construct an 'AdditionalData' value from a hexadecimal-encoded+-- 'StrictByteString' that you have obtained on your own, usually from the+-- network or disk.+additionalDataFromHexByteString+ :: Base16 StrictByteString+ -> Either AdditionalDataHexDecodingError AdditionalData+additionalDataFromHexByteString hexBs =+ case Base16.decodeBase16Untyped (Base16.extractBase16 hexBs) of+ Left err -> Left (AdditionalDataHexDecodingError err)+ Right bs -> Right (AdditionalData bs)+ -- | An encrypted message. It is guaranteed to be of size: -- @original_message_length + 'cryptoSecretStreamXChaCha20Poly1305ABytes'@ -- -- @since 0.0.1.0-data CipherText = CipherText+data Ciphertext = Ciphertext { messageLength :: CULLong- , cipherTextForeignPtr :: ForeignPtr CUChar+ , ciphertextForeignPtr :: ForeignPtr CUChar } -- | -- -- @since 0.0.1.0-instance Eq CipherText where- (CipherText cipherTextLength1 h1) == (CipherText cipherTextLength2 h2) =- unsafeDupablePerformIO $ do- result1 <-+instance Eq Ciphertext where+ (Ciphertext ciphertextLength1 h1) == (Ciphertext ciphertextLength2 h2) =+ let+ textLength = ciphertextLength1 == ciphertextLength2+ content = foreignPtrEq h1 h2- (fromIntegral cipherTextLength1 + cryptoSecretStreamXChaCha20Poly1305ABytes)- pure $ cipherTextLength1 == cipherTextLength2 && result1+ (fromIntegral ciphertextLength1 + cryptoSecretStreamXChaCha20Poly1305ABytes)+ in+ textLength && content -- | @since 0.0.1.0-instance Ord CipherText where- compare (CipherText cipherTextLength1 c1) (CipherText cipherTextLength2 c2) =- unsafeDupablePerformIO $ do- result1 <- foreignPtrOrd c1 c2 (fromIntegral cipherTextLength1 + cryptoSecretStreamXChaCha20Poly1305ABytes)- pure $ compare cipherTextLength1 cipherTextLength2 <> result1+instance Ord Ciphertext where+ compare (Ciphertext ciphertextLength1 c1) (Ciphertext ciphertextLength2 c2) =+ let+ textLength = compare ciphertextLength1 ciphertextLength2+ content =+ foreignPtrOrd+ c1+ c2+ (fromIntegral ciphertextLength1 + cryptoSecretStreamXChaCha20Poly1305ABytes)+ in+ textLength <> content -- | @since 0.0.1.0-instance Display CipherText where+instance Display Ciphertext where displayBuilder = Builder.fromText . Base16.extractBase16 . ciphertextToHexText -- | @since 0.0.1.0-instance Show CipherText where+instance Show Ciphertext where show = BSI.unpackChars . Base16.extractBase16 . ciphertextToHexByteString --- | Create a 'CipherText' from a binary 'StrictByteString' that you have obtained on your own,--- usually from the network or disk. It must be a valid hash built from the concatenation+-- | Create a 'Ciphertext' from a binary 'StrictByteString' that you have obtained on your own,+-- usually from the network or disk. It must be a valid ciphertext built from the concatenation -- of the encrypted message and the authentication tag. ----- The input hash must at least of length 'cryptoSecretStreamXChaCha20Poly1305ABytes'+-- The input ciphertext must at least of length 'cryptoSecretStreamXChaCha20Poly1305ABytes' -- -- @since 0.0.1.0-ciphertextFromHexByteString :: Base16 StrictByteString -> Either Text CipherText-ciphertextFromHexByteString hexCipherText = unsafeDupablePerformIO $- case Base16.decodeBase16Untyped (Base16.extractBase16 hexCipherText) of+ciphertextFromHexByteString :: Base16 StrictByteString -> Either Text Ciphertext+ciphertextFromHexByteString hexCiphertext = unsafeDupablePerformIO $+ case Base16.decodeBase16Untyped (Base16.extractBase16 hexCiphertext) of Right bytestring -> if BS.length bytestring >= fromIntegral cryptoSecretStreamXChaCha20Poly1305ABytes- then BSU.unsafeUseAsCStringLen bytestring $ \(outsideCipherTextPtr, outsideCipherTextLength) -> do- cipherTextFPtr <- BSI.mallocByteString @CChar outsideCipherTextLength -- The foreign pointer that will receive the hash data.- Foreign.withForeignPtr cipherTextFPtr $ \cipherTextPtr ->- -- We copy bytes from 'outsideCipherTextPtr' to 'cipherTextPtr.- Foreign.copyArray cipherTextPtr outsideCipherTextPtr outsideCipherTextLength+ then BSU.unsafeUseAsCStringLen bytestring $ \(outsideCiphertextPtr, outsideCiphertextLength) -> do+ ciphertextFPtr <- BSI.mallocByteString @CChar outsideCiphertextLength -- The foreign pointer that will receive the ciphertext data.+ Foreign.withForeignPtr ciphertextFPtr $ \ciphertextPtr ->+ -- We copy bytes from 'outsideCiphertextPtr' to 'ciphertextPtr.+ Foreign.copyArray ciphertextPtr outsideCiphertextPtr outsideCiphertextLength pure $ Right $- CipherText- (fromIntegral @Int @CULLong outsideCipherTextLength - fromIntegral @CSize @CULLong cryptoSecretStreamXChaCha20Poly1305ABytes)- (Foreign.castForeignPtr @CChar @CUChar cipherTextFPtr)- else pure $ Left $ Text.pack "CipherText is too short"+ Ciphertext+ (fromIntegral @Int @CULLong outsideCiphertextLength - fromIntegral @CSize @CULLong cryptoSecretStreamXChaCha20Poly1305ABytes)+ (Foreign.castForeignPtr @CChar @CUChar ciphertextFPtr)+ else pure $ Left $ Text.pack "Ciphertext is too short" Left msg -> pure $ Left msg --- | Convert a 'CipherText' to a hexadecimal-encoded 'Text'.+-- | Convert a 'Ciphertext' to a hexadecimal-encoded 'Text'. -- -- ⚠️ Be prudent as to where you store it! -- -- @since 0.0.1.0-ciphertextToHexText :: CipherText -> Base16 Text+ciphertextToHexText :: Ciphertext -> Base16 Text ciphertextToHexText = Base16.encodeBase16 . ciphertextToBinary --- | Convert a 'CipherText' to a hexadecimal-encoded 'StrictByteString'.+-- | Convert a 'Ciphertext' to a hexadecimal-encoded 'StrictByteString' in constant time. -- -- ⚠️ Be prudent as to where you store it! -- -- @since 0.0.1.0-ciphertextToHexByteString :: CipherText -> Base16 StrictByteString-ciphertextToHexByteString = Base16.encodeBase16' . ciphertextToBinary+ciphertextToHexByteString :: Ciphertext -> Base16 StrictByteString+ciphertextToHexByteString (Ciphertext ciphertextLength fPtr) =+ Base16.assertBase16 $ binaryToHex fPtr (cryptoSecretStreamXChaCha20Poly1305ABytes + fromIntegral ciphertextLength) --- | Convert a 'CipherText' to a binary 'StrictByteString'.+-- | Convert a 'Ciphertext' to a binary 'StrictByteString' in constant time. -- -- ⚠️ Be prudent as to where you store it! -- -- @since 0.0.1.0-ciphertextToBinary :: CipherText -> StrictByteString-ciphertextToBinary (CipherText cipherTextLength fPtr) =+ciphertextToBinary :: Ciphertext -> StrictByteString+ciphertextToBinary (Ciphertext ciphertextLength fPtr) = BSI.fromForeignPtr0 (Foreign.castForeignPtr fPtr)- (fromIntegral cipherTextLength + fromIntegral cryptoSecretStreamXChaCha20Poly1305ABytes)+ (fromIntegral ciphertextLength + fromIntegral cryptoSecretStreamXChaCha20Poly1305ABytes) -- | @since 0.0.1.0 data StreamEncryptionException = StreamEncryptionException
test/Test/HMAC.hs view
@@ -2,11 +2,12 @@ module Test.HMAC where -import qualified Sel.HMAC.SHA256 as SHA256-import qualified Sel.HMAC.SHA256 as SHA512-import qualified Sel.HMAC.SHA256 as SHA512_256 import Test.Tasty import Test.Tasty.HUnit++import qualified Sel.HMAC.SHA256 as SHA256+import qualified Sel.HMAC.SHA512 as SHA512+import qualified Sel.HMAC.SHA512_256 as SHA512_256 import TestUtils (assertRight) spec :: TestTree@@ -41,7 +42,7 @@ testSingleHMACSHA256Hashing :: Assertion testSingleHMACSHA256Hashing = do key <- SHA256.newAuthenticationKey- tag <- SHA256.authenticate "Hello, world!" key+ let tag = SHA256.authenticate "Hello, world!" key assertBool "message is verified" $ SHA256.verify tag key "Hello, world!" @@ -66,7 +67,7 @@ testHMAC256AuthenticationTagSerialisation :: Assertion testHMAC256AuthenticationTagSerialisation = do key <- SHA256.newAuthenticationKey- tag1 <- SHA256.authenticate "Hello, world!" key+ let tag1 = SHA256.authenticate "Hello, world!" key let hexTag = SHA256.authenticationTagToHexByteString tag1 tag2 <- assertRight $ SHA256.authenticationTagFromHexByteString hexTag assertEqual "Roundtripping authentication key" tag1 tag2@@ -76,7 +77,7 @@ testSingleHMACSHA512Hashing :: Assertion testSingleHMACSHA512Hashing = do key <- SHA512.newAuthenticationKey- tag <- SHA512.authenticate "Hello, world!" key+ let tag = SHA512.authenticate "Hello, world!" key assertBool "message is verified" $ SHA512.verify tag key "Hello, world!" @@ -88,7 +89,7 @@ SHA512.updateMultipart multipart "2" assertEqual "HMAC-SHA512 tag is consistent"- "be884a372976dd92e819d55ea7090d0b87377b3ac0773a97a5fdc12523104c35"+ "7aad3ea0ca427425ba2fc3cf8078d31e94a62483b7ead624825f9a3fe36bbf5aaf8276e8876faef1a84226e439466774ebc7062495b19a6811cc376bfcccede0" (SHA512.authenticationTagToHexByteString actual) testHMAC512AuthenticationKeySerialisation :: Assertion@@ -101,7 +102,7 @@ testHMAC512AuthenticationTagSerialisation :: Assertion testHMAC512AuthenticationTagSerialisation = do key <- SHA512.newAuthenticationKey- tag1 <- SHA512.authenticate "Hello, world!" key+ let tag1 = SHA512.authenticate "Hello, world!" key let hexTag = SHA512.authenticationTagToHexByteString tag1 tag2 <- assertRight $ SHA512.authenticationTagFromHexByteString hexTag assertEqual "Roundtripping authentication key" tag1 tag2@@ -111,7 +112,7 @@ testSingleHMACSHA512_256Hashing :: Assertion testSingleHMACSHA512_256Hashing = do key <- SHA512_256.newAuthenticationKey- tag <- SHA512_256.authenticate "Hello, world!" key+ let tag = SHA512_256.authenticate "Hello, world!" key assertBool "message is verified" $ SHA512_256.verify tag key "Hello, world!" @@ -123,7 +124,7 @@ SHA512_256.updateMultipart multipart "2" assertEqual "HMAC-SHA512_256 tag is consistent"- "be884a372976dd92e819d55ea7090d0b87377b3ac0773a97a5fdc12523104c35"+ "7aad3ea0ca427425ba2fc3cf8078d31e94a62483b7ead624825f9a3fe36bbf5a" (SHA512_256.authenticationTagToHexByteString actual) testHMAC512_256AuthenticationKeySerialisation :: Assertion@@ -136,7 +137,7 @@ testHMAC512_256AuthenticationTagSerialisation :: Assertion testHMAC512_256AuthenticationTagSerialisation = do key <- SHA512_256.newAuthenticationKey- tag1 <- SHA512_256.authenticate "Hello, world!" key+ let tag1 = SHA512_256.authenticate "Hello, world!" key let hexTag = SHA512_256.authenticationTagToHexByteString tag1 tag2 <- assertRight $ SHA512_256.authenticationTagFromHexByteString hexTag assertEqual "Roundtripping authentication key" tag1 tag2
test/Test/Hashing.hs view
@@ -4,15 +4,15 @@ module Test.Hashing where import Control.Monad (void)- import qualified Data.ByteString.Unsafe as BS import Foreign hiding (void) import Foreign.C import LibSodium.Bindings.GenericHashing (cryptoGenericHash, cryptoGenericHashBytes)-import qualified Sel.Hashing as Hashing import Test.Tasty import Test.Tasty.HUnit +import qualified Sel.Hashing as Hashing+ spec :: TestTree spec = testGroup@@ -24,7 +24,7 @@ testCryptoGenericHashWithoutKey :: Assertion testCryptoGenericHashWithoutKey = do- expected <- Hashing.hashToHexByteString <$> Hashing.hashByteString Nothing "test test"+ let expected = Hashing.hashToHexByteString $ Hashing.hashByteString Nothing "test test" assertEqual "Hashed test string is consistent without key" expected@@ -54,7 +54,7 @@ testMultipartHahsing :: Assertion testMultipartHahsing = do hashKey <- Hashing.newHashKey- expectedHash <- Hashing.hashByteString (Just hashKey) "test test"+ let expectedHash = Hashing.hashByteString (Just hashKey) "test test" actualHash <- Hashing.withMultipart (Just hashKey) $ \multipartState -> do let message1 = "test " Hashing.updateMultipart multipartState message1
test/Test/Hashing/Password.hs view
@@ -6,10 +6,11 @@ import Data.Maybe (isNothing) import Data.Text (Text) import qualified Data.Text as Text-import qualified Sel.Hashing.Password as Sel import Test.Tasty import Test.Tasty.HUnit +import qualified Sel.Hashing.Password as Sel+ spec :: TestTree spec = testGroup@@ -45,8 +46,8 @@ cmpPWHashes = on (==) Sel.passwordHashToByteString salt1 <- Sel.genSalt- hashOrig <- hashWSalt salt1 password- hashOrig' <- hashWSalt salt1 password+ let hashOrig = hashWSalt salt1 password+ hashOrig' = hashWSalt salt1 password assertBool "Password hashing with salt is consistent" (cmpPWHashes hashOrig hashOrig')@@ -57,7 +58,7 @@ (not $ cmpPWHashes hashOrig hashWoSalt) salt2 <- Sel.genSalt- hashWNewSalt <- hashWSalt salt2 password+ let hashWNewSalt = hashWSalt salt2 password assertBool "Password hashing differs with a new salt" (not $ cmpPWHashes hashOrig hashWNewSalt)
test/Test/Hashing/SHA2.hs view
@@ -3,11 +3,12 @@ module Test.Hashing.SHA2 where import Data.Text (Text)-import qualified Sel.Hashing.SHA256 as SHA256-import qualified Sel.Hashing.SHA512 as SHA512 import Test.Tasty import Test.Tasty.HUnit +import qualified Sel.Hashing.SHA256 as SHA256+import qualified Sel.Hashing.SHA512 as SHA512+ spec :: TestTree spec = testGroup@@ -27,7 +28,7 @@ testSingleHashSHA512 :: Assertion testSingleHashSHA512 = do let password = "hunter2" :: Text- actual <- SHA512.hashText password+ actual = SHA512.hashText password assertEqual "SHA-512 hashing is consistent" (SHA512.hashToHexByteString actual)@@ -46,7 +47,7 @@ testSingleHashSHA256 :: Assertion testSingleHashSHA256 = do let password = "hunter2" :: Text- actual <- SHA256.hashText password+ actual = SHA256.hashText password assertEqual "SHA-256 hashing is consistent" (SHA256.hashToHexByteString actual)
test/Test/Hashing/Short.hs view
@@ -4,10 +4,11 @@ import Data.Maybe (fromJust) import Data.Text (Text)-import qualified Sel.Hashing.Short as Short import Test.Tasty import Test.Tasty.HUnit +import qualified Sel.Hashing.Short as Short+ spec :: TestTree spec = testGroup@@ -19,7 +20,7 @@ testHashPassword = do let key = fromJust $ Short.hexTextToShortHashKey "9301a3c5eedf2d783b72dc41fb907964" let input = "kwak kwak" :: Text- hash <- Short.hashText key input+ let hash = Short.hashText key input assertEqual "input hashing is consistent" (Short.shortHashToHexText hash)
test/Test/PublicKey/Cipher.hs view
@@ -2,9 +2,10 @@ module Test.PublicKey.Cipher where -import Sel.PublicKey.Cipher import Test.Tasty import Test.Tasty.HUnit++import Sel.PublicKey.Cipher import TestUtils spec :: TestTree@@ -14,7 +15,7 @@ [ testCase "Encrypt a message with public-key encryption" testEncryptMessage , testCase "Round-trip nonce serialisation" testNonceSerdeRoundtrip , testCase "Round-trip keys serialisation" testKeysSerdeRoundtrip- , testCase "Round-trip cipher text serialisation" testCipherTextSerdeRoundtrip+ , testCase "Round-trip cipher text serialisation" testCiphertextSerdeRoundtrip ] testEncryptMessage :: Assertion@@ -45,10 +46,10 @@ (pk2, sk2) <- assertRight $ keyPairFromHexByteStrings hexPk hexSk assertEqual "Roundtripping keys serialisation" (pk1, sk1) (pk2, sk2) -testCipherTextSerdeRoundtrip :: Assertion-testCipherTextSerdeRoundtrip = do+testCiphertextSerdeRoundtrip :: Assertion+testCiphertextSerdeRoundtrip = do (publicKey, secretKey) <- newKeyPair- (_, cipherText) <- encrypt "hello hello" publicKey secretKey- let hexCipherText = cipherTextToHexByteString cipherText- cipherText2 <- assertRight $ cipherTextFromHexByteString hexCipherText- assertEqual "Roundtripping cipher text serialisation" cipherText cipherText2+ (_, ciphertext) <- encrypt "hello hello" publicKey secretKey+ let hexCiphertext = ciphertextToHexByteString ciphertext+ ciphertext2 <- assertRight $ ciphertextFromHexByteString hexCiphertext+ assertEqual "Roundtripping cipher text serialisation" ciphertext ciphertext2
test/Test/PublicKey/Seal.hs view
@@ -2,10 +2,11 @@ module Test.PublicKey.Seal where -import Sel.PublicKey.Cipher-import Sel.PublicKey.Seal import Test.Tasty import Test.Tasty.HUnit++import Sel.PublicKey.Cipher+import Sel.PublicKey.Seal spec :: TestTree spec =
test/Test/PublicKey/Signature.hs view
@@ -2,9 +2,10 @@ module Test.PublicKey.Signature where -import Sel.PublicKey.Signature import Test.Tasty import Test.Tasty.HUnit++import Sel.PublicKey.Signature spec :: TestTree spec =
test/Test/Scrypt.hs view
@@ -4,9 +4,10 @@ module Test.Scrypt where import Data.ByteString-import Sel.Scrypt import Test.Tasty import Test.Tasty.HUnit++import Sel.Scrypt spec :: TestTree spec =
test/Test/SecretKey/Authentication.hs view
@@ -2,9 +2,10 @@ module Test.SecretKey.Authentication where -import Sel.SecretKey.Authentication import Test.Tasty import Test.Tasty.HUnit++import Sel.SecretKey.Authentication import TestUtils (assertRight) spec :: TestTree@@ -19,7 +20,7 @@ testAuthenticateMessage :: Assertion testAuthenticateMessage = do key <- newAuthenticationKey- tag <- authenticate "hello, world" key+ let tag = authenticate "hello, world" key assertBool "Tag verified" (verify tag key "hello, world")@@ -37,8 +38,8 @@ testAuthTagSerdeRoundtrip :: Assertion testAuthTagSerdeRoundtrip = do key <- newAuthenticationKey- expectedTag <- authenticate "hello, world" key- let hexTag = authenticationTagToHexByteString expectedTag+ let expectedTag = authenticate "hello, world" key+ hexTag = authenticationTagToHexByteString expectedTag actualTag <- assertRight $ authenticationTagFromHexByteString hexTag assertEqual "Tag is expected"
test/Test/SecretKey/Cipher.hs view
@@ -2,9 +2,10 @@ module Test.SecretKey.Cipher where -import Sel.SecretKey.Cipher import Test.Tasty import Test.Tasty.HUnit++import Sel.SecretKey.Cipher import TestUtils (assertRight) spec :: TestTree@@ -14,7 +15,7 @@ [ testCase "Encrypt a message with a secret key and a nonce" testEncryptMessage , testCase "Round-trip nonce serialisation" testNonceSerdeRoundtrip , testCase "Round-trip secret key serialisation" testSecretKeySerdeRoundtrip- , testCase "Round-trip hash serialisation" testHashSerdeRoundtrip+ , testCase "Round-trip ciphertext serialisation" testCiphertextSerdeRoundtrip ] testEncryptMessage :: Assertion@@ -40,9 +41,9 @@ secretKey2 <- assertRight $ secretKeyFromHexByteString . unsafeSecretKeyToHexByteString $ secretKey assertEqual "Roundtripping secret key" secretKey secretKey2 -testHashSerdeRoundtrip :: Assertion-testHashSerdeRoundtrip = do+testCiphertextSerdeRoundtrip :: Assertion+testCiphertextSerdeRoundtrip = do secretKey <- newSecretKey- (_, hash) <- encrypt "" secretKey- hash2 <- assertRight $ hashFromHexByteString . hashToHexByteString $ hash- assertEqual "Roundtripping hash" hash hash2+ (_, ciphertext) <- encrypt "" secretKey+ ciphertext2 <- assertRight $ ciphertextFromHexByteString . ciphertextToHexByteString $ ciphertext+ assertEqual "Roundtripping ciphertext" ciphertext ciphertext2
test/Test/SecretKey/Stream.hs view
@@ -3,9 +3,10 @@ module Test.SecretKey.Stream where import Data.ByteString (StrictByteString)-import qualified Sel.SecretKey.Stream as Stream import Test.Tasty import Test.Tasty.HUnit++import qualified Sel.SecretKey.Stream as Stream import TestUtils spec :: TestTree@@ -14,7 +15,7 @@ "Secret Key Encrypted Stream tests" [ testCase "Encrypt a stream with a secret key" testEncryptStream , testCase "Round-trip secret key serialisation" testSecretKeySerdeRoundtrip- , testCase "Round-trip ciphertext serialisation" testCipherTextSerdeRoundtrip+ , testCase "Round-trip ciphertext serialisation" testCiphertextSerdeRoundtrip -- , testCase "Round-trip header serialisation" testHeaderSerdeRoundtrip ] @@ -22,8 +23,11 @@ testEncryptStream = do secretKey <- Stream.newSecretKey let messages = ["Hello", "abcdf", "world"]- (header, cipherTexts) <- Stream.encryptList secretKey messages- mResult <- Stream.decryptList secretKey header cipherTexts+ ad = map Just [Stream.AdditionalData "Goodbye", Stream.AdditionalData "31337", Stream.AdditionalData "planet"]+ messagesAndAd = zip ad messages+ (header, ciphertexts) <- Stream.encryptList secretKey messagesAndAd+ let ciphertextsAndAd = zip ad ciphertexts+ mResult <- Stream.decryptList secretKey header ciphertextsAndAd result <- assertJust mResult assertEqual@@ -42,15 +46,16 @@ secretKey1 secretKey2 -testCipherTextSerdeRoundtrip :: Assertion-testCipherTextSerdeRoundtrip = do+testCiphertextSerdeRoundtrip :: Assertion+testCiphertextSerdeRoundtrip = do secretKey <- Stream.newSecretKey let message = "hello" :: StrictByteString+ additionalData = Stream.AdditionalData "this is additional data" (_, encryptedPayload1) <- Stream.encryptStream secretKey $ \multipart -> do- Stream.encryptChunk multipart Stream.Final message+ Stream.encryptChunk multipart Stream.Final (Just additionalData) message - let hexCipherText = Stream.ciphertextToHexByteString encryptedPayload1- encryptedPayload2 <- assertRight $ Stream.ciphertextFromHexByteString hexCipherText+ let hexCiphertext = Stream.ciphertextToHexByteString encryptedPayload1+ encryptedPayload2 <- assertRight $ Stream.ciphertextFromHexByteString hexCiphertext assertEqual "The ciphertexts remain equal"