  SPDX - FileCopyrightText : 2020 
--
  SPDX - License - Identifier : MPL-2.0

-- | Internals of @crypto_sign@.
module NaCl.Sign.Internal
  ( SecretKey
  , toSecretKey
  , PublicKey
  , toPublicKey
  , keypair

  , create
  , open
  ) where

import Prelude hiding (length)

import Data.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes, allocRet, length, withByteArray)
import Data.ByteArray.Sized (SizedByteArray, sizedByteArray)
import Data.ByteString (ByteString)
import Data.Functor (void)
import Data.Proxy (Proxy (Proxy))
import Foreign.Ptr (nullPtr)

import qualified Data.ByteArray.Sized as Sized (alloc, allocRet)
import qualified Libsodium as Na


-- | Secret key that can be used for creating a signature.
--
-- This type is parametrised by the actual data type that contains
-- bytes. This can be, for example, a @ByteString@, but, since this
-- is a secret key, it is better to use @ScrubbedBytes@.
type SecretKey a = SizedByteArray Na.CRYPTO_SIGN_SECRETKEYBYTES a

-- | Convert bytes to a secret key.
toSecretKey :: ByteArrayAccess bytes => bytes -> Maybe (SecretKey bytes)
toSecretKey = sizedByteArray

-- | Public key that can be used for verifyiing a signature.
--
-- This type is parametrised by the actual data type that contains
  bytes . This can be , for example , a @ByteString@.
type PublicKey a = SizedByteArray Na.CRYPTO_SIGN_PUBLICKEYBYTES a

-- | Convert bytes to a public key.
toPublicKey :: ByteArrayAccess bytes => bytes -> Maybe (PublicKey bytes)
toPublicKey = sizedByteArray

  | Generate a new ' SecretKey ' together with its ' PublicKey ' .
--
-- Note: this function is not thread-safe (since the underlying
  C function is not thread - safe both in Sodium and in NaCl ) !
-- Either make sure there are no concurrent calls or see
  @Crypto . Sodium . Init@ in
-- <-sodium crypto-sodium>
-- to learn how to make this function thread-safe.
keypair :: IO (PublicKey ByteString, SecretKey ScrubbedBytes)
keypair = do
  (pk, sk) <-
    Sized.allocRet Proxy $ \skPtr ->
    Sized.alloc $ \pkPtr ->
    -- always returns 0, so we don’t check it
    void $ Na.crypto_sign_keypair pkPtr skPtr
  pure (pk, sk)


-- | Sign a message.
create
  ::  ( ByteArrayAccess skBytes
      , ByteArrayAccess pt, ByteArray ct
      )
  ^ Signer ’s secret key
  -> pt  -- ^ Message to sign
  -> IO ct
create sk msg = do
    (_ret, ct) <-
      allocRet clen $ \ctPtr ->
      withByteArray sk $ \skPtr ->
      withByteArray msg $ \msgPtr -> do
        Na.crypto_sign ctPtr nullPtr
          msgPtr (fromIntegral $ length msg)
          skPtr
  _ ret can be only 0 , so we do n’t check it
    -- TODO: Actually, it looks like this function can fail and return
    -- a -1, even though this is not documented :/.
    pure ct
  where
    clen :: Int
    clen = fromIntegral Na.crypto_sign_bytes + length msg


-- | Verify the signature of a signed message.
open
  ::  ( ByteArrayAccess pkBytes
      , ByteArray pt, ByteArrayAccess ct
      )
  ^ Signer ’s public key
  -> ct  -- ^ Signed message
  -> IO (Maybe pt)
open pk ct = do
    (ret, msg) <-
      allocRet mlen $ \msgPtr ->
      withByteArray pk $ \pkPtr ->
      withByteArray ct $ \ctPtr -> do
        Na.crypto_sign_open msgPtr nullPtr
          ctPtr (fromIntegral $ length ct)
          pkPtr
    if ret == 0 then
      pure $ Just msg
    else
      pure Nothing
  where
    mlen :: Int
    mlen = length ct - fromIntegral Na.crypto_sign_bytes


 | Internals of @crypto_sign@.
 | Secret key that can be used for creating a signature.

 This type is parametrised by the actual data type that contains
 bytes. This can be, for example, a @ByteString@, but, since this
 is a secret key, it is better to use @ScrubbedBytes@.
 | Convert bytes to a secret key.
 | Public key that can be used for verifyiing a signature.

 This type is parametrised by the actual data type that contains
 | Convert bytes to a public key.

 Note: this function is not thread-safe (since the underlying
 Either make sure there are no concurrent calls or see
 <-sodium crypto-sodium>
 to learn how to make this function thread-safe.
 always returns 0, so we don’t check it
 | Sign a message.
 ^ Message to sign
 TODO: Actually, it looks like this function can fail and return
 a -1, even though this is not documented :/.
 | Verify the signature of a signed message.
 ^ Signed message
  SPDX - FileCopyrightText : 2020 
  SPDX - License - Identifier : MPL-2.0

module NaCl.Sign.Internal
  ( SecretKey
  , toSecretKey
  , PublicKey
  , toPublicKey
  , keypair

  , create
  , open
  ) where

import Prelude hiding (length)

import Data.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes, allocRet, length, withByteArray)
import Data.ByteArray.Sized (SizedByteArray, sizedByteArray)
import Data.ByteString (ByteString)
import Data.Functor (void)
import Data.Proxy (Proxy (Proxy))
import Foreign.Ptr (nullPtr)

import qualified Data.ByteArray.Sized as Sized (alloc, allocRet)
import qualified Libsodium as Na


type SecretKey a = SizedByteArray Na.CRYPTO_SIGN_SECRETKEYBYTES a

toSecretKey :: ByteArrayAccess bytes => bytes -> Maybe (SecretKey bytes)
toSecretKey = sizedByteArray

  bytes . This can be , for example , a @ByteString@.
type PublicKey a = SizedByteArray Na.CRYPTO_SIGN_PUBLICKEYBYTES a

toPublicKey :: ByteArrayAccess bytes => bytes -> Maybe (PublicKey bytes)
toPublicKey = sizedByteArray

  | Generate a new ' SecretKey ' together with its ' PublicKey ' .
  C function is not thread - safe both in Sodium and in NaCl ) !
  @Crypto . Sodium . Init@ in
keypair :: IO (PublicKey ByteString, SecretKey ScrubbedBytes)
keypair = do
  (pk, sk) <-
    Sized.allocRet Proxy $ \skPtr ->
    Sized.alloc $ \pkPtr ->
    void $ Na.crypto_sign_keypair pkPtr skPtr
  pure (pk, sk)


create
  ::  ( ByteArrayAccess skBytes
      , ByteArrayAccess pt, ByteArray ct
      )
  ^ Signer ’s secret key
  -> IO ct
create sk msg = do
    (_ret, ct) <-
      allocRet clen $ \ctPtr ->
      withByteArray sk $ \skPtr ->
      withByteArray msg $ \msgPtr -> do
        Na.crypto_sign ctPtr nullPtr
          msgPtr (fromIntegral $ length msg)
          skPtr
  _ ret can be only 0 , so we do n’t check it
    pure ct
  where
    clen :: Int
    clen = fromIntegral Na.crypto_sign_bytes + length msg


open
  ::  ( ByteArrayAccess pkBytes
      , ByteArray pt, ByteArrayAccess ct
      )
  ^ Signer ’s public key
  -> IO (Maybe pt)
open pk ct = do
    (ret, msg) <-
      allocRet mlen $ \msgPtr ->
      withByteArray pk $ \pkPtr ->
      withByteArray ct $ \ctPtr -> do
        Na.crypto_sign_open msgPtr nullPtr
          ctPtr (fromIntegral $ length ct)
          pkPtr
    if ret == 0 then
      pure $ Just msg
    else
      pure Nothing
  where
    mlen :: Int
    mlen = length ct - fromIntegral Na.crypto_sign_bytes
