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

-- |
-- Module: LibSodium.Bindings.Signing
-- Description: Direct bindings to the public-key signing algorithm ed25519 implemented in Libsodium
-- Copyright: (C) Hécate Moonlight 2022
-- License: BSD-3-Clause
-- Maintainer: The Haskell Cryptography Group
-- Stability: Stable
-- Portability: GHC only
module LibSodium.Bindings.CryptoSign
  ( -- * Introduction
    -- $introduction

    -- * Key pair generation
    cryptoSignKeyPair
  , cryptoSignSeedKeyPair

    -- * Combined mode
  , cryptoSign
  , cryptoSignOpen

    -- * Detached Mode
    -- $detachedMode
  , cryptoSignDetached
  , cryptoSignVerifyDetached

    -- * Multi-part messages
    -- $mpm
  , CryptoSignState
  , withSignState
  , cryptoSignInit
  , cryptoSignUpdate
  , cryptoSignFinalCreate
  , cryptoSignFinalVerify
  , cryptoSignED25519SkToSeed
  , cryptoSignED25519SkToPk

    -- * Constants
  , cryptoSignStateBytes
  , cryptoSignPublicKeyBytes
  , cryptoSignSecretKeyBytes
  , cryptoSignBytes
  , cryptoSignSeedBytes
  )
where

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

-- $introduction
--
-- When signing with public-key cryptography, a signer generates a key pair consisting of:
--
--   * A secret key, which you can use to append a signature to any number of messages.
--   * A public key, which anybody can use to verify that the signature appended to a
--     message was issued by the creator of the public key.
--
-- Verifiers need to already know and ultimately trust a public key before messages signed using
-- it can be verified.
--
-- Warning: this is different from authenticated encryption. Appending a signature does not change
-- the representation of the message itself.

-------------------------
-- Key pair generation --
-------------------------

-- | Randomly generate a secret key and a corresponding public key.
--
-- /See:/ [crypto_sign_keypair()](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#key-pair-generation)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_sign_keypair"
  cryptoSignKeyPair
    :: Ptr CUChar
    -- ^ A pointer to the buffer holding the public key. It has a length of 'cryptoSignPublicKeyBytes' bytes.
    -> Ptr CUChar
    -- ^ A pointer to the buffer holding the secret key. It has a length of 'cryptoSignSecretKeyBytes' bytes.
    -> IO CInt
    -- ^ Returns 0 on success, -1 on error.

-- | Derive a keypair (secret key and public key) from a seed.
-- It is deterministic.
--
-- /See:/ [crypto_sign_seed_keypair()](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#key-pair-generation)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_sign_seed_keypair"
  cryptoSignSeedKeyPair
    :: Ptr CUChar
    -- ^ A pointer to the buffer holding the public key. It has a length of 'cryptoSignPublicKeyBytes'.
    -> Ptr CUChar
    -- ^ A pointer to the buffer holding the secret key. It has a length of 'cryptoSignSecretKeyBytes'.
    -> Ptr CUChar
    -- ^ A pointer to the seed. It has a length of 'cryptoSignSeedBytes'.
    -> IO CInt
    -- ^ Returns 0 on success, -1 on error.

-------------------
-- Combined Mode --
-------------------

-- | Prepend a signature to a message, using the secret key.
--
-- The signed message, which includes the signature plus an unaltered copy of the message, is put
-- into the signed message buffer, and is of length 'cryptoSignBytes' + @length of the message@ bytes.
--
-- If the pointer to the length of the signed message is not a 'Foreign.nullPtr',
-- then the actual length of the signed message is stored in it.
--
-- /See:/ [crypto_sign()](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#combined-mode)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_sign"
  cryptoSign
    :: Ptr CUChar
    -- ^ Pointer to the signed message.
    -> Ptr CULLong
    -- ^ Pointer to the length of the signed message.
    -> Ptr CUChar
    -- ^ Pointer to the message to sign.
    -> CULLong
    -- ^ Length of the message.
    -> Ptr CUChar
    -- ^ Pointer to the secret key.
    -> IO CInt
    -- ^ Returns 0 on success, -1 on error.

-- | Check that the signed message has a valid signature for the public key.
--
-- On success, it puts the message, without the signature into the first buffer.
-- The length of the message will be stored in the , if the pointer is not a 'Foreign.nullPtr'.
--
-- /See:/ [crypto_sign_open()](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#key-pair-generation)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_sign_open"
  cryptoSignOpen
    :: Ptr CUChar
    -- ^ Pointer to the buffer holding the message without the signature.
    -> Ptr CULLong
    -- ^ Pointer to the buffer holding the length of the message, if it
    -- is not a 'Foreign.nullPtr'.
    -> Ptr CUChar
    -- ^ Pointer to the signed message.
    -> CULLong
    -- ^ Length of the signed message.
    -> Ptr CUChar
    -- ^ Pointer to the public key.
    -> IO CInt
    -- ^ On success, the function returns 0
    -- If the signature isn't valid, then the function returns -1.

-------------------
-- Detached Mode --
-------------------

-- $detachedMode
--
-- In detached mode, the signature is stored without attaching a copy of the original message to it.

-- | Sign the message using the secret key and put the signature into a buffer, which can be up to
-- 'cryptoSignBytes' bytes long.
-- The actual length of the signature is put into a buffer if its pointer is not 'Foreign.nullPtr'.
-- It is safe to ignore the length of the signature and always consider a signature as 'cryptoSignBytes' bytes long;
-- shorter signatures will be transparently padded with zeros if necessary.
--
-- /See:/ [crypto_sign_detached()](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#detached-mode)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_sign_detached"
  cryptoSignDetached
    :: Ptr CUChar
    -- ^ Pointer to the signature.
    -> Ptr CULLong
    -- ^ Pointer to the length of the signature.
    -> Ptr CUChar
    -- ^ Pointer to the message to sign.
    -> CULLong
    -- ^ Length of the message.
    -> Ptr CUChar
    -- ^ Pointer to the secret key.
    -> IO CInt
    -- ^ Returns 0 on success, -1 on error.

-- | Verify that the signature is valid for the message, using the
-- signer's public key.
--
-- /See:/ [crypto_sign_verify_detached()](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#detached-mode)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_sign_verify_detached"
  cryptoSignVerifyDetached
    :: Ptr CUChar
    -- ^ Pointer to the signature
    -> Ptr CUChar
    -- ^ Pointer to the message
    -> CULLong
    -- ^ Length of the message
    -> Ptr CUChar
    -- ^ Pointer to the signer's public key
    -> IO CInt
    -- ^ Returns 0 on success, -1 on error.

-------------------------
-- Multi-part messages --
-------------------------

-- $mpm
-- If the message you're trying to sign doesn't fit in memory, then it can be provided as a sequence
-- of arbitrarily-sized chunks.
-- This uses the @Ed25519ph@ signature system, which pre-hashes the message. In other words,
-- what gets signed is not the message itself but its image through a hash function. If the message
-- can fit in memory and be supplied as a single chunk, then the single-part API should be
-- preferred.
--
-- == Note
--
-- @Ed25519ph(m)@ is intentionally not equivalent to @Ed25519(SHA512(m))@. If, for
-- some reason, you need to pre-hash the message yourself, then use the multi-part
-- 'LibSodium.Bindings.GenericHashing' module and sign the 512-bit output.

-- | Opaque tag representing the hash state struct @crypto_sign_state@ used by the C API.
--
-- It is of size 'cryptoSignStateBytes'.
--
-- To use a 'CryptoSignState', use 'withSignState'.
--
-- @since 0.0.1.0
data CryptoSignState

-- | Perform an operation with a 'CryptoSignState' of size 'cryptoSignStateBytes' allocated
-- and deallocated automatically.
--
-- ⚠️ The return value of 'withSignState' __MUST NOT__ leak the 'CryptoSignState'.
--
-- Please refer to the documentation of 'Foreign.allocaBytes' for more operational details.
--
-- @since 0.0.1.0
withSignState :: (Ptr CryptoSignState -> IO a) -> IO a
withSignState :: forall a. (Ptr CryptoSignState -> IO a) -> IO a
withSignState Ptr CryptoSignState -> IO a
action = do
  let Int
size :: Int = CSize -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral CSize
cryptoSignStateBytes
  Int -> (Ptr CryptoSignState -> IO a) -> IO a
forall a b. Int -> (Ptr a -> IO b) -> IO b
allocaBytes Int
size Ptr CryptoSignState -> IO a
action

-- | Initialise the 'CryptoSignState' state.
--
-- It must be called before the first 'cryptoSignUpdate' call.
--
-- /See:/ [crypto_sign_init()](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#multi-part-messages)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_sign_init"
  cryptoSignInit
    :: Ptr CryptoSignState
    -- ^ A pointer to the cryptographic state. Cannot be 'Foreign.nullPtr'.
    -> IO CInt
    -- ^ Returns 0 on success, -1 on error.

-- | Add a new chunk to the message that will eventually be signed.
--
-- After all parts have been supplied, 'cryptoSignFinalCreate' or 'cryptoSignFinalVerify'
-- can be used.
--
-- /See:/ [crypto_sign_update()](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#multi-part-messages)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_sign_update"
  cryptoSignUpdate
    :: Ptr CryptoSignState
    -- ^ A pointer to an initialized cryptographic state. Cannot be 'Foreign.nullPtr'.
    -> Ptr CUChar
    -- ^ Pointer to the new chunk to sign.
    -> CULLong
    -- ^ Length of the new chunk.
    -> IO CInt
    -- ^ Returns 0 on success, -1 on error.

-- | Compute a signature for the previously supplied message
-- using the secret key, and put it into the signature buffer.
--
-- If the pointer to the length of the signature is not a 'Foreign.nullPtr',
-- then the length of the signature is stored at this address.
-- It is safe to ignore the length of the signature and always consider
-- a signature as 'cryptoSignBytes' bytes long;
-- shorter signatures will be transparently padded with zeros if necessary.
--
-- /See:/ [crypto_sign_final_create()](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#multi-part-messages)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_sign_final_create"
  cryptoSignFinalCreate
    :: Ptr CryptoSignState
    -- ^ A pointer to an initialized cryptographic state. Cannot be 'Foreign.nullPtr'.
    -> Ptr CUChar
    -- ^ Pointer to the signature. Cannot be 'Foreign.nullPtr'.
    -> Ptr CULLong
    -- ^ A pointer to the length of the signature. Can be 'Foreign.nullPtr'.
    -> Ptr CUChar
    -- ^ Pointer to the secret key. Cannot be 'Foreign.nullPtr'.
    -> IO CInt
    -- ^ Returns 0 on success, -1 on error.

-- | Verify that the signature is valid using the public key
-- for the message whose content has been previously supplied using 'cryptoSignUpdate'.
--
-- /See:/ [crypto_sign_final_verify()](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#multi-part-messages)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_sign_final_verify"
  cryptoSignFinalVerify
    :: Ptr CryptoSignState
    -- ^ A pointer to an initialized cryptographic state. Cannot be 'Foreign.nullPtr'.
    -> Ptr CUChar
    -- ^ Pointer to the signature.
    -> Ptr CUChar
    -- ^ Pointer to the public key.
    -> IO CInt
    -- ^ Returns 0 on success, -1 on error.

-- | This function extracts the seed from the
-- secret key secret key and copies it into the buffer holding the seed.
-- The size of the seed will be equal to 'cryptoSignSeedBytes'.
--
-- /See:/ [crypto_sign_ed25519_sk_to_seed()](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#extracting-the-seed-and-the-public-key-from-the-secret-key)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_sign_ed25519_sk_to_seed"
  cryptoSignED25519SkToSeed
    :: Ptr CUChar
    -- ^ Pointer to the seed.
    -> Ptr CUChar
    -- ^ Pointer to the secret key.
    -> IO CInt
    -- ^ Returns 0 on success, -1 on error.

-- | This function extracts the public key from the secret key secret key
-- and copies it into public key.
-- The size of public key will be equal to 'cryptoSignPublicKeyBytes'.
--
-- /See:/ [crypto_sign_ed25519_sk_to_pk()](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#extracting-the-seed-and-the-public-key-from-the-secret-key)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_sign_ed25519_sk_to_pk"
  cryptoSignED25519SkToPk
    :: Ptr CUChar
    -- ^ Pointer to the public key.
    -> Ptr CUChar
    -- ^ Pointer to the secret key.
    -> IO CInt
    -- ^ Returns 0 on success, -1 on error.

---------------
-- Constants --
---------------

-- | The amount of memory needed to store a 'CryptoSignState'.
--
-- /See:/ [crypto_sign_statebytes](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#constants)
--
-- @since 0.0.1.0
foreign import capi "sodium.h crypto_sign_statebytes"
  cryptoSignStateBytes :: CSize

-- |
--
-- /See:/ [crypto_sign_PUBLICKEYBYTES](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#constants)
--
-- @since 0.0.1.0
foreign import capi "sodium.h value crypto_sign_PUBLICKEYBYTES"
  cryptoSignPublicKeyBytes :: CSize

-- |
--
-- /See:/ [crypto_sign_SECRETKEYBYTES](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#constants)
--
-- @since 0.0.1.0
foreign import capi "sodium.h value crypto_sign_SECRETKEYBYTES"
  cryptoSignSecretKeyBytes :: CSize

-- |
--
-- /See:/ [crypto_sign_BYTES](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#constants)
--
-- @since 0.0.1.0
foreign import capi "sodium.h value crypto_sign_BYTES"
  cryptoSignBytes :: CSize

-- |
--
-- /See:/ [crypto_sign_SEEDBYTES](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#constants)
--
-- @since 0.0.1.0
foreign import capi "sodium.h value crypto_sign_SEEDBYTES"
  cryptoSignSeedBytes :: CSize