{-# LANGUAGE CApiFFI #-}
{-# LANGUAGE ScopedTypeVariables #-}

-- |
-- Module: LibSodium.Bindings.SecretStream
-- Description: Direct bindings to the stream and file encryption primitives of libsodium
-- Copyright: (C) Hécate Moonlight 2022
-- License: BSD-3-Clause
-- Maintainer: The Haskell Cryptography Group
-- Stability: Stable
-- Portability: GHC only
module LibSodium.Bindings.SecretStream
  ( -- * Introduction
    -- $introduction

    -- * Usage
    -- $usage
    CryptoSecretStreamXChaCha20Poly1305State
  , withCryptoSecretStreamXChaCha20Poly1305State

    -- * Encryption
  , cryptoSecretStreamXChaCha20Poly1305KeyGen
  , cryptoSecretStreamXChaCha20Poly1305InitPush
  , cryptoSecretStreamXChaCha20Poly1305Push

    -- * Decryption
  , cryptoSecretStreamXChaCha20Poly1305InitPull
  , cryptoSecretStreamXChaCha20Poly1305Pull

    -- * Rekeying
  , cryptoSecretStreamXChaCha20Poly1305Rekey

    -- * Constants

    -- ** Key, Header and State size constants
  , cryptoSecretStreamXChaCha20Poly1305KeyBytes
  , cryptoSecretStreamXChaCha20Poly1305HeaderBytes
  , cryptoSecretStreamXChaCha20Poly1305StateBytes
  , cryptoSecretStreamXChaCha20Poly1305ABytes
  , cryptoSecretStreamXChaCha20Poly1305MessageBytesMax

    -- ** Tag constants
  , cryptoSecretStreamXChaCha20Poly1305TagMessage
  , cryptoSecretStreamXChaCha20Poly1305TagPush
  , cryptoSecretStreamXChaCha20Poly1305TagRekey
  , cryptoSecretStreamXChaCha20Poly1305TagFinal
  ) where

import Foreign (Ptr)
import Foreign.C (CInt (CInt), CSize (CSize), CUChar (CUChar), CULLong (CULLong))
import Foreign.Marshal (allocaBytes)

-- $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:
--
-- * Messages cannot be truncated, removed, reordered, duplicated or modified without this being detected by the decryption functions.
-- * The same sequence encrypted twice will produce different ciphertexts.
-- * An authentication tag is added to each encrypted message: stream corruption will be detected early, without having to read the stream until the end.
-- * Each message can include additional data (ex: timestamp, protocol version) in the computation of the authentication tag.
-- * Messages can have different sizes.
-- * There are no practical limits to the total length of the stream, or to the total number of individual messages.
-- * Ratcheting: at any point in the stream, it is possible to "forget" the secret key used to encrypt the previous messages, and switch to a new key.

-- $usage
-- An encrypted stream starts with a short header, whose size is 'cryptoSecretStreamXChaCha20Poly1305HeaderBytes' bytes.
-- That header must be sent/stored before the sequence of encrypted messages, as it is required to decrypt the stream.
-- The header content doesn't have to be secret and decryption with a different header would fail.
--
-- A tag is attached to each message. That tag can be any of:
--
-- * 0, or 'cryptoSecretStreamXChaCha20Poly1305TagMessage': the most common tag, that doesn't add any information about the nature of the message.
--
-- * 'cryptoSecretStreamXChaCha20Poly1305TagFinal': indicates that the message marks the end of the stream, and erases the secret key used to encrypt the previous sequence.
--
-- * 'cryptoSecretStreamXChaCha20Poly1305TagPush': indicates that the message marks the end of a set of messages, but not the end of the stream.
--   For example, a huge JSON string sent as multiple chunks can use this tag to indicate to the application that the string is complete and that it
--   can be decoded. But the stream itself is not closed, and more data may follow.
--
-- * 'cryptoSecretStreamXChaCha20Poly1305TagRekey': "forget" the secret key used to encrypt this message and the previous ones, and derive a new secret key.
--
-- A typical encrypted stream simply attaches 0 as a tag to all messages, except the last one which is tagged as 'cryptoSecretStreamXChaCha20Poly1305TagFinal'.
--
-- Note that tags are encrypted; encrypted streams do not reveal any information about sequence boundaries
-- ('cryptoSecretStreamXChaCha20Poly1305TagPush' and 'cryptoSecretStreamXChaCha20Poly1305TagRekey' tags).
--
-- For each message, additional data can be included in the computation of the authentication tag.
-- With this API, additional data is rarely required, and most applications can just use 'Foreign.Ptr.nullPtr' and a length of 0 instead.

-- | Opaque tag representing the hash state struct @crypto_secretstream_xchacha20poly1305_state@ used by the C API.
--
-- To use a 'CryptoSecretStreamXChaCha20Poly1305State', use 'withCryptoSecretStreamXChaCha20Poly1305State'.
--
-- @since 0.0.1.0
data CryptoSecretStreamXChaCha20Poly1305State

-- | Allocate an opaque 'CryptoSecretStreamXChaCha20Poly1305State' of size 'cryptoSecretStreamXChaCha20Poly1305StateBytes'.
--
-- ⚠️ Do not leak the 'CryptoSecretStreamXChaCha20Poly1305State' outside of the lambda,
-- otherwise you will point at deallocated memory!
--
-- @since 0.0.1.0
withCryptoSecretStreamXChaCha20Poly1305State :: (Ptr CryptoSecretStreamXChaCha20Poly1305State -> IO a) -> IO a
withCryptoSecretStreamXChaCha20Poly1305State :: forall a.
(Ptr CryptoSecretStreamXChaCha20Poly1305State -> IO a) -> IO a
withCryptoSecretStreamXChaCha20Poly1305State Ptr CryptoSecretStreamXChaCha20Poly1305State -> IO a
action = Int
-> (Ptr CryptoSecretStreamXChaCha20Poly1305State -> IO a) -> IO a
forall a b. Int -> (Ptr a -> IO b) -> IO b
allocaBytes (CSize -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral CSize
cryptoSecretStreamXChaCha20Poly1305StateBytes) Ptr CryptoSecretStreamXChaCha20Poly1305State -> IO a
action

-- === Encryption ===

-- | Create a random secret key to encrypt a stream, and store it into the parameter
--
-- Note that using this function is not required to obtain a suitable key:
-- the secretstream API can use any secret key whose size is
-- 'cryptoSecretStreamXChaCha20Poly1305KeyBytes' bytes.
--
-- /See:/ [crypto_secretstream_xchacha20poly1305_keygen()](https://doc.libsodium.org/secret-key_cryptography/secretstream#encryption)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_secretstream_xchacha20poly1305_keygen"
  cryptoSecretStreamXChaCha20Poly1305KeyGen
    :: Ptr CUChar
    -- ^ Pointer in which the secret key will be stored
    -> IO ()

-- | Initialise the cryptographic state using the secret key, then stores the stream header into the header buffer
--
-- /See:/ [crypto_secretstream_xchacha20poly1305_init_push()](https://doc.libsodium.org/secret-key_cryptography/secretstream#encryption)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_secretstream_xchacha20poly1305_init_push"
  cryptoSecretStreamXChaCha20Poly1305InitPush
    :: Ptr CryptoSecretStreamXChaCha20Poly1305State
    -- ^ Cryptographic state
    -> Ptr CUChar
    -- ^ Header buffer, must be of size 'cryptoSecretStreamXChaCha20Poly1305HeaderBytes'.
    -> Ptr CUChar
    -- ^ Buffer holding the secret key. Must be of size 'cryptoSecretStreamXChaCha20Poly1305KeyBytes'.
    -> IO CInt
    -- ^ Returns 0 on success, -1 on error.

-- | Encrypt a message using a cryptographic state and a tag.
--
-- Additional data can be optionally provided.
-- The maximum length of an individual message is 'cryptoSecretStreamXChaCha20Poly1305MessageBytesMax' bytes (~ 256 GB).
--
-- /See:/ [crypto_secretstream_xchacha20poly1305_push()](https://doc.libsodium.org/secret-key_cryptography/secretstream#encryption)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_secretstream_xchacha20poly1305_push"
  cryptoSecretStreamXChaCha20Poly1305Push
    :: Ptr CryptoSecretStreamXChaCha20Poly1305State
    -- ^ Cryptographic state
    -> Ptr CUChar
    -- ^ Buffer that receives the cipher text.
    -> Ptr CULLong
    -- ^ If this pointer is not 'Foreign.Ptr.nullPtr', it will store the length of the cipher text.
    -- It is guaranteed to be always @(messageLength + 'cryptoSecretStreamXChaCha20Poly1305ABytes' )@.
    -> Ptr CUChar
    -- ^ Pointer to the message to encrypt
    -> CULLong
    -- ^ Length of the message (@messageLength@).
    -> Ptr CUChar
    -- ^ Additional, optional data that can be included in the computation. Can be 'Foreign.Ptr.nullPtr' if you have nothing to add.
    -> CULLong
    -- ^ Length of the additional, optional data. Can be 0 if you have nothing to add.
    -> CUChar
    -- ^ Tag for the cipher text.
    -> IO CInt
    -- ^ Returns 0 on success, -1 on error.

-- === Decryption ===

-- | Initialise the cryptographic state using the secret key and a header.
-- The secret key will not be required any more for subsequent operations
--
-- @since 0.0.1.0
--
-- /See:/ [crypto_secretstream_xchacha20poly1305_init_pull()](https://doc.libsodium.org/secret-key_cryptography/secretstream#decryption)
foreign import capi "sodium.h crypto_secretstream_xchacha20poly1305_init_pull"
  cryptoSecretStreamXChaCha20Poly1305InitPull
    :: Ptr CryptoSecretStreamXChaCha20Poly1305State
    -- ^ Cryptographic state
    -> Ptr CUChar
    -- ^ Header buffer, must be of size 'cryptoSecretStreamXChaCha20Poly1305HeaderBytes'.
    -> Ptr CUChar
    -- ^ Buffer holding the secret key. Must be of size 'cryptoSecretStreamXChaCha20Poly1305KeyBytes'.
    -> IO CInt
    -- ^ Returns 0 on success, -1 if the header is invalid.

-- | Decrypt a message chunk.
--
-- Applications will typically call this function in a loop, until a message with the
-- 'cryptoSecretStreamXChaCha20Poly1305TagFinal' tag is found.
--
-- /See:/ [crypto_secretstream_xchacha20poly1305_pull()](https://doc.libsodium.org/secret-key_cryptography/secretstream#decryption)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_secretstream_xchacha20poly1305_pull"
  cryptoSecretStreamXChaCha20Poly1305Pull
    :: Ptr CryptoSecretStreamXChaCha20Poly1305State
    -- ^ Cryptographic state.
    -> Ptr CUChar
    -- ^ Buffer that will hold the decrypted message.
    -> Ptr CULLong
    -- ^ If this pointer is not 'Foreign.Ptr.nullPtr', it will store the length of the message.
    -- It is guaranteed to be always @(cipherTextLength - 'cryptoSecretStreamXChaCha20Poly1305ABytes' )@.
    -> Ptr CUChar
    -- ^ If this pointer is not 'Foreign.Ptr.nullPtr', the tag attached to the message is stored in that buffer.
    -> Ptr CUChar
    -- ^ Cipher text to be decrypted.
    -> CULLong
    -- ^ Length in bytes of the cipher text.
    -> Ptr CUChar
    -- ^ Additional, optional data that was bundled with the cipher text will be put there. Can be 'Foreign.Ptr.nullPtr' if you know that nothing was added.
    -> CULLong
    -- ^ Length of the additional, optional data. Can be 0 if you have nothing to add.
    -> IO CInt
    -- ^ Return 0 on success, -1 if the ciphertext appears to be invalid.

-- === Rekeying ===

-- | Rekeying happens automatically and transparently, before the internal counter of the underlying cipher wraps. Therefore, streams can be arbitrary large.
--
-- Optionally, applications for which forward secrecy is critical can attach the 'cryptoSecretStreamXChaCha20Poly1305TagRekey'
-- tag to a message in order to trigger an explicit rekeying.
--
-- The decryption API will automatically update the secret key if this tag is found attached to a message.
-- Explicit rekeying can also be performed without adding a tag, by calling this function.
--
-- This updates the state, but doesn't add any information about the secret key change to the stream.
-- If this function is used to create an encrypted stream, the decryption process must call
-- that function at the exact same stream location.
--
-- /See:/ [crypto_secretstream_xchacha20poly1305_rekey()](https://doc.libsodium.org/secret-key_cryptography/secretstream#rekeying)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_secretstream_xchacha20poly1305_rekey"
  cryptoSecretStreamXChaCha20Poly1305Rekey
    :: Ptr CryptoSecretStreamXChaCha20Poly1305State
    -- ^ Cryptographic state.
    -> IO ()

-- === Constants ===

-- | Size of the secret key
--
-- /See:/ [crypto_secretstream_xchacha20poly1305_KEYBYTES](https://doc.libsodium.org/secret-key_cryptography/secretstream#constants)
--
-- @since 0.0.1.0
foreign import capi "sodium.h value crypto_secretstream_xchacha20poly1305_KEYBYTES"
  cryptoSecretStreamXChaCha20Poly1305KeyBytes :: CSize

-- | Size of the encryption header
--
-- /See:/ [crypto_secretstream_xchacha20poly1305_HEADERBYTES](https://doc.libsodium.org/secret-key_cryptography/secretstream#constants)
--
-- @since 0.0.1.0
foreign import capi "sodium.h value crypto_secretstream_xchacha20poly1305_HEADERBYTES"
  cryptoSecretStreamXChaCha20Poly1305HeaderBytes :: CSize

-- | Size of an opaque 'CryptoSecretStreamXChaCha20Poly1305State'
--
-- /See:/ [crypto_secretstream_xchacha20poly1305_statebytes](https://doc.libsodium.org/secret-key_cryptography/secretstream#constants)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_secretstream_xchacha20poly1305_statebytes"
  cryptoSecretStreamXChaCha20Poly1305StateBytes :: CSize

-- | Size of an authentication tag in bytes
--
-- /See:/ [crypto_secretstream_xchacha20poly1305_ABYTES](https://doc.libsodium.org/secret-key_cryptography/secretstream#constants)
--
-- @since 0.0.1.0
foreign import capi "sodium.h value crypto_secretstream_xchacha20poly1305_ABYTES"
  cryptoSecretStreamXChaCha20Poly1305ABytes :: CSize

-- | Maximum length of an invidual message in bytes (~ 256 GB)
--
-- /See:/ [crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX](https://doc.libsodium.org/secret-key_cryptography/secretstream#constants)
--
-- @since 0.0.1.0
foreign import capi "sodium.h value crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX"
  cryptoSecretStreamXChaCha20Poly1305MessageBytesMax :: CSize

-- | Most common tag, add no information about the nature of the message
--
-- /See:/ [crypto_secretstream_xchacha20poly1305_TAG_MESSAGE](https://doc.libsodium.org/secret-key_cryptography/secretstream#constants)
--
-- @since 0.0.1.0
foreign import capi "sodium.h value crypto_secretstream_xchacha20poly1305_TAG_MESSAGE"
  cryptoSecretStreamXChaCha20Poly1305TagMessage :: CSize

-- | Indicates that the message marks the end of a set of messages, but not the end of the stream
--
-- /See:/ [crypto_secretstream_xchacha20poly1305_TAG_PUSH](https://doc.libsodium.org/secret-key_cryptography/secretstream#constants)
--
-- @since 0.0.1.0
foreign import capi "sodium.h value crypto_secretstream_xchacha20poly1305_TAG_PUSH"
  cryptoSecretStreamXChaCha20Poly1305TagPush :: CSize

-- | "forget" the secret key used to encrypt this message and the previous ones, and derive a new secret key.
--
-- /See:/ [crypto_secretstream_xchacha20poly1305_TAG_REKEY](https://doc.libsodium.org/secret-key_cryptography/secretstream#constants)
--
-- @since 0.0.1.0
foreign import capi "sodium.h value crypto_secretstream_xchacha20poly1305_TAG_REKEY"
  cryptoSecretStreamXChaCha20Poly1305TagRekey :: CSize

-- | Marks the end of the stream, and erases the secret key used to encrypt the previous sequence.
--
-- /See:/ [crypto_secretstream_xchacha20poly1305_TAG_FINAL](https://doc.libsodium.org/secret-key_cryptography/secretstream#constants)
--
-- @since 0.0.1.0
foreign import capi "sodium.h value crypto_secretstream_xchacha20poly1305_TAG_FINAL"
  cryptoSecretStreamXChaCha20Poly1305TagFinal :: CSize