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

{-# OPTIONS_HADDOCK not-home #-}

-- ! This module merely re-exports definitions from the corresponding
-- ! module in NaCl and alters the Haddock to make it more specific
-- ! to crypto-sodium. So, the docs should be kept more-or-less in sync.

-- | Public-key signatures.
--
-- It is best to import this module qualified:
--
-- @
-- import qualified Crypto.Sodium.Sign as Sign
--
-- signed = Sign.'create' sk message
-- verified = Sign.'open' pk signed
-- @
--
-- Functions in this modules work with /combined/ signatures.
-- This means that when you sign a message, it will be copied as is
-- and then a signature will be prepended. So you should treat the
-- resulting value as a transparent (because it is not encrypted)
-- package with a signature attached on top.
--
-- Instead of accessing the message directly, you should use
-- 'open', which will verify the signature and return a copy of the
-- original message only if the signature was valid.
module Crypto.Sodium.Sign
  (
  -- * Keys
    PublicKey
  , toPublicKey
  , SecretKey
  , toSecretKey
  , keypair

  , keypairFromSeed
  , unsafeKeypairFromSeed

  -- * Signing/verifying
  , create
  , open
  ) where

import Data.ByteArray (ByteArrayAccess, ScrubbedBytes, withByteArray)
import Data.ByteString (ByteString)
import Data.ByteArray.Sized (SizedByteArray, alloc, allocRet)
import Data.Functor (void)
import Data.Proxy (Proxy(..))
import System.IO.Unsafe (unsafePerformIO)

import qualified Libsodium as Na

import NaCl.Sign
  (PublicKey, SecretKey, create, keypair, open, toPublicKey, toSecretKey)

-- | Seed for deterministically generating a keypair.
--
-- In accordance with Libsodium's documentation, the seed must be of size
-- @Na.CRYPTO_SIGN_SEEDBYTES@.
--
-- This type is parametrised by the actual data type that contains
-- bytes. This can be, for example, a @ByteString@.
type Seed a = SizedByteArray Na.CRYPTO_SIGN_SEEDBYTES a


-- | Generate a new 'SecretKey' together with its 'PublicKey' from a given seed.
keypairFromSeed
  :: ByteArrayAccess seed
  => Seed seed
  -> IO (PublicKey ByteString, SecretKey ScrubbedBytes)
keypairFromSeed :: Seed seed -> IO (PublicKey ByteString, SecretKey ScrubbedBytes)
keypairFromSeed Seed seed
seed = do
  Proxy 64
-> (Ptr CUChar -> IO (PublicKey ByteString))
-> IO (PublicKey ByteString, SecretKey ScrubbedBytes)
forall (n :: Nat) c p a.
ByteArrayN n c =>
Proxy n -> (Ptr p -> IO a) -> IO (a, c)
allocRet Proxy 64
forall k (t :: k). Proxy t
Proxy ((Ptr CUChar -> IO (PublicKey ByteString))
 -> IO (PublicKey ByteString, SecretKey ScrubbedBytes))
-> (Ptr CUChar -> IO (PublicKey ByteString))
-> IO (PublicKey ByteString, SecretKey ScrubbedBytes)
forall a b. (a -> b) -> a -> b
$ \Ptr CUChar
skPtr ->
    (Ptr CUChar -> IO ()) -> IO (PublicKey ByteString)
forall (n :: Nat) ba p.
(ByteArrayN n ba, KnownNat n) =>
(Ptr p -> IO ()) -> IO ba
alloc ((Ptr CUChar -> IO ()) -> IO (PublicKey ByteString))
-> (Ptr CUChar -> IO ()) -> IO (PublicKey ByteString)
forall a b. (a -> b) -> a -> b
$ \Ptr CUChar
pkPtr ->
    Seed seed -> (Ptr CUChar -> IO ()) -> IO ()
forall ba p a. ByteArrayAccess ba => ba -> (Ptr p -> IO a) -> IO a
withByteArray Seed seed
seed ((Ptr CUChar -> IO ()) -> IO ()) -> (Ptr CUChar -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Ptr CUChar
sdPtr ->
    -- always returns 0, so we don’t check it
    IO CInt -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO CInt -> IO ()) -> IO CInt -> IO ()
forall a b. (a -> b) -> a -> b
$ Ptr CUChar -> Ptr CUChar -> Ptr CUChar -> IO CInt
forall k1 k2 k3 (pk :: k1) (sk :: k2) (seed :: k3).
Ptr CUChar -> Ptr CUChar -> Ptr CUChar -> IO CInt
Na.crypto_sign_seed_keypair Ptr CUChar
pkPtr Ptr CUChar
skPtr Ptr CUChar
sdPtr

-- | Generate a new 'SecretKey' together with its 'PublicKey' from a given seed,
-- in a pure context.
unsafeKeypairFromSeed
  :: ByteArrayAccess seed
  => Seed seed
  -> (PublicKey ByteString, SecretKey ScrubbedBytes)
unsafeKeypairFromSeed :: Seed seed -> (PublicKey ByteString, SecretKey ScrubbedBytes)
unsafeKeypairFromSeed = IO (PublicKey ByteString, SecretKey ScrubbedBytes)
-> (PublicKey ByteString, SecretKey ScrubbedBytes)
forall a. IO a -> a
unsafePerformIO (IO (PublicKey ByteString, SecretKey ScrubbedBytes)
 -> (PublicKey ByteString, SecretKey ScrubbedBytes))
-> (Seed seed
    -> IO (PublicKey ByteString, SecretKey ScrubbedBytes))
-> Seed seed
-> (PublicKey ByteString, SecretKey ScrubbedBytes)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Seed seed -> IO (PublicKey ByteString, SecretKey ScrubbedBytes)
forall seed.
ByteArrayAccess seed =>
Seed seed -> IO (PublicKey ByteString, SecretKey ScrubbedBytes)
keypairFromSeed