{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE UndecidableInstances #-}

-- | Shelley CLI option data types and functions for cryptographic keys.
module Cardano.CLI.Shelley.Key
  ( VerificationKeyOrFile (..)
  , readVerificationKeyOrFile
  , readVerificationKeyOrTextEnvFile

  , VerificationKeyTextOrFile (..)
  , VerificationKeyTextOrFileError (..)
  , readVerificationKeyTextOrFileAnyOf
  , renderVerificationKeyTextOrFileError

  , VerificationKeyOrHashOrFile (..)
  , readVerificationKeyOrHashOrFile
  , readVerificationKeyOrHashOrTextEnvFile

  , PaymentVerifier(..)
  , StakeVerifier(..)

  , generateKeyPair
  ) where

import           Cardano.Api
import           Cardano.Prelude

import qualified Data.ByteString as BS
import qualified Data.List.NonEmpty as NE
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text

import           Cardano.CLI.Types


------------------------------------------------------------------------------
-- Verification key deserialisation
------------------------------------------------------------------------------

-- | Either a verification key or path to a verification key file.
data VerificationKeyOrFile keyrole
  = VerificationKeyValue !(VerificationKey keyrole)
  -- ^ A verification key.
  | VerificationKeyFilePath !VerificationKeyFile
  -- ^ A path to a verification key file.
  -- Note that this file hasn't been validated at all (whether it exists,
  -- contains a key of the correct type, etc.)

deriving instance Show (VerificationKey keyrole)
  => Show (VerificationKeyOrFile keyrole)

deriving instance Eq (VerificationKey keyrole)
  => Eq (VerificationKeyOrFile keyrole)

-- | Read a verification key or verification key file and return a
-- verification key.
--
-- If a filepath is provided, the file can either be formatted as Bech32, hex,
-- or text envelope.
readVerificationKeyOrFile
  :: ( HasTextEnvelope (VerificationKey keyrole)
     , SerialiseAsBech32 (VerificationKey keyrole)
     )
  => AsType keyrole
  -> VerificationKeyOrFile keyrole
  -> IO (Either (FileError InputDecodeError) (VerificationKey keyrole))
readVerificationKeyOrFile :: AsType keyrole
-> VerificationKeyOrFile keyrole
-> IO
     (Either (FileError InputDecodeError) (VerificationKey keyrole))
readVerificationKeyOrFile AsType keyrole
asType VerificationKeyOrFile keyrole
verKeyOrFile =
  case VerificationKeyOrFile keyrole
verKeyOrFile of
    VerificationKeyValue VerificationKey keyrole
vk -> Either (FileError InputDecodeError) (VerificationKey keyrole)
-> IO
     (Either (FileError InputDecodeError) (VerificationKey keyrole))
forall (f :: * -> *) a. Applicative f => a -> f a
pure (VerificationKey keyrole
-> Either (FileError InputDecodeError) (VerificationKey keyrole)
forall a b. b -> Either a b
Right VerificationKey keyrole
vk)
    VerificationKeyFilePath (VerificationKeyFile String
fp) ->
      AsType (VerificationKey keyrole)
-> NonEmpty (InputFormat (VerificationKey keyrole))
-> String
-> IO
     (Either (FileError InputDecodeError) (VerificationKey keyrole))
forall a.
AsType a
-> NonEmpty (InputFormat a)
-> String
-> IO (Either (FileError InputDecodeError) a)
readKeyFile
        (AsType keyrole -> AsType (VerificationKey keyrole)
forall a. AsType a -> AsType (VerificationKey a)
AsVerificationKey AsType keyrole
asType)
        ([InputFormat (VerificationKey keyrole)]
-> NonEmpty (InputFormat (VerificationKey keyrole))
forall a. [a] -> NonEmpty a
NE.fromList [InputFormat (VerificationKey keyrole)
forall a. SerialiseAsBech32 a => InputFormat a
InputFormatBech32, InputFormat (VerificationKey keyrole)
forall a. SerialiseAsRawBytes a => InputFormat a
InputFormatHex, InputFormat (VerificationKey keyrole)
forall a. HasTextEnvelope a => InputFormat a
InputFormatTextEnvelope])
        String
fp

-- | Read a verification key or verification key file and return a
-- verification key.
--
-- If a filepath is provided, it will be interpreted as a text envelope
-- formatted file.
readVerificationKeyOrTextEnvFile
  :: HasTextEnvelope (VerificationKey keyrole)
  => AsType keyrole
  -> VerificationKeyOrFile keyrole
  -> IO (Either (FileError InputDecodeError) (VerificationKey keyrole))
readVerificationKeyOrTextEnvFile :: AsType keyrole
-> VerificationKeyOrFile keyrole
-> IO
     (Either (FileError InputDecodeError) (VerificationKey keyrole))
readVerificationKeyOrTextEnvFile AsType keyrole
asType VerificationKeyOrFile keyrole
verKeyOrFile =
  case VerificationKeyOrFile keyrole
verKeyOrFile of
    VerificationKeyValue VerificationKey keyrole
vk -> Either (FileError InputDecodeError) (VerificationKey keyrole)
-> IO
     (Either (FileError InputDecodeError) (VerificationKey keyrole))
forall (f :: * -> *) a. Applicative f => a -> f a
pure (VerificationKey keyrole
-> Either (FileError InputDecodeError) (VerificationKey keyrole)
forall a b. b -> Either a b
Right VerificationKey keyrole
vk)
    VerificationKeyFilePath (VerificationKeyFile String
fp) ->
      AsType (VerificationKey keyrole)
-> String
-> IO
     (Either (FileError InputDecodeError) (VerificationKey keyrole))
forall a.
HasTextEnvelope a =>
AsType a -> String -> IO (Either (FileError InputDecodeError) a)
readKeyFileTextEnvelope (AsType keyrole -> AsType (VerificationKey keyrole)
forall a. AsType a -> AsType (VerificationKey a)
AsVerificationKey AsType keyrole
asType) String
fp

data PaymentVerifier
  = PaymentVerifierKey VerificationKeyTextOrFile
  | PaymentVerifierScriptFile ScriptFile
  deriving (PaymentVerifier -> PaymentVerifier -> Bool
(PaymentVerifier -> PaymentVerifier -> Bool)
-> (PaymentVerifier -> PaymentVerifier -> Bool)
-> Eq PaymentVerifier
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: PaymentVerifier -> PaymentVerifier -> Bool
$c/= :: PaymentVerifier -> PaymentVerifier -> Bool
== :: PaymentVerifier -> PaymentVerifier -> Bool
$c== :: PaymentVerifier -> PaymentVerifier -> Bool
Eq, Int -> PaymentVerifier -> ShowS
[PaymentVerifier] -> ShowS
PaymentVerifier -> String
(Int -> PaymentVerifier -> ShowS)
-> (PaymentVerifier -> String)
-> ([PaymentVerifier] -> ShowS)
-> Show PaymentVerifier
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [PaymentVerifier] -> ShowS
$cshowList :: [PaymentVerifier] -> ShowS
show :: PaymentVerifier -> String
$cshow :: PaymentVerifier -> String
showsPrec :: Int -> PaymentVerifier -> ShowS
$cshowsPrec :: Int -> PaymentVerifier -> ShowS
Show)

data StakeVerifier
  = StakeVerifierKey (VerificationKeyOrFile StakeKey)
  | StakeVerifierScriptFile ScriptFile
  deriving (StakeVerifier -> StakeVerifier -> Bool
(StakeVerifier -> StakeVerifier -> Bool)
-> (StakeVerifier -> StakeVerifier -> Bool) -> Eq StakeVerifier
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: StakeVerifier -> StakeVerifier -> Bool
$c/= :: StakeVerifier -> StakeVerifier -> Bool
== :: StakeVerifier -> StakeVerifier -> Bool
$c== :: StakeVerifier -> StakeVerifier -> Bool
Eq, Int -> StakeVerifier -> ShowS
[StakeVerifier] -> ShowS
StakeVerifier -> String
(Int -> StakeVerifier -> ShowS)
-> (StakeVerifier -> String)
-> ([StakeVerifier] -> ShowS)
-> Show StakeVerifier
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [StakeVerifier] -> ShowS
$cshowList :: [StakeVerifier] -> ShowS
show :: StakeVerifier -> String
$cshow :: StakeVerifier -> String
showsPrec :: Int -> StakeVerifier -> ShowS
$cshowsPrec :: Int -> StakeVerifier -> ShowS
Show)

-- | Either an unvalidated text representation of a verification key or a path
-- to a verification key file.
data VerificationKeyTextOrFile
  = VktofVerificationKeyText !Text
  | VktofVerificationKeyFile !VerificationKeyFile
  deriving (VerificationKeyTextOrFile -> VerificationKeyTextOrFile -> Bool
(VerificationKeyTextOrFile -> VerificationKeyTextOrFile -> Bool)
-> (VerificationKeyTextOrFile -> VerificationKeyTextOrFile -> Bool)
-> Eq VerificationKeyTextOrFile
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: VerificationKeyTextOrFile -> VerificationKeyTextOrFile -> Bool
$c/= :: VerificationKeyTextOrFile -> VerificationKeyTextOrFile -> Bool
== :: VerificationKeyTextOrFile -> VerificationKeyTextOrFile -> Bool
$c== :: VerificationKeyTextOrFile -> VerificationKeyTextOrFile -> Bool
Eq, Int -> VerificationKeyTextOrFile -> ShowS
[VerificationKeyTextOrFile] -> ShowS
VerificationKeyTextOrFile -> String
(Int -> VerificationKeyTextOrFile -> ShowS)
-> (VerificationKeyTextOrFile -> String)
-> ([VerificationKeyTextOrFile] -> ShowS)
-> Show VerificationKeyTextOrFile
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [VerificationKeyTextOrFile] -> ShowS
$cshowList :: [VerificationKeyTextOrFile] -> ShowS
show :: VerificationKeyTextOrFile -> String
$cshow :: VerificationKeyTextOrFile -> String
showsPrec :: Int -> VerificationKeyTextOrFile -> ShowS
$cshowsPrec :: Int -> VerificationKeyTextOrFile -> ShowS
Show)

-- | An error in deserialising a 'VerificationKeyTextOrFile' to a
-- 'VerificationKey'.
data VerificationKeyTextOrFileError
  = VerificationKeyTextError !InputDecodeError
  | VerificationKeyFileError !(FileError InputDecodeError)
  deriving Int -> VerificationKeyTextOrFileError -> ShowS
[VerificationKeyTextOrFileError] -> ShowS
VerificationKeyTextOrFileError -> String
(Int -> VerificationKeyTextOrFileError -> ShowS)
-> (VerificationKeyTextOrFileError -> String)
-> ([VerificationKeyTextOrFileError] -> ShowS)
-> Show VerificationKeyTextOrFileError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [VerificationKeyTextOrFileError] -> ShowS
$cshowList :: [VerificationKeyTextOrFileError] -> ShowS
show :: VerificationKeyTextOrFileError -> String
$cshow :: VerificationKeyTextOrFileError -> String
showsPrec :: Int -> VerificationKeyTextOrFileError -> ShowS
$cshowsPrec :: Int -> VerificationKeyTextOrFileError -> ShowS
Show

-- | Render an error message for a 'VerificationKeyTextOrFileError'.
renderVerificationKeyTextOrFileError :: VerificationKeyTextOrFileError -> Text
renderVerificationKeyTextOrFileError :: VerificationKeyTextOrFileError -> Text
renderVerificationKeyTextOrFileError VerificationKeyTextOrFileError
vkTextOrFileErr =
  case VerificationKeyTextOrFileError
vkTextOrFileErr of
    VerificationKeyTextError InputDecodeError
err -> InputDecodeError -> Text
renderInputDecodeError InputDecodeError
err
    VerificationKeyFileError FileError InputDecodeError
err -> String -> Text
Text.pack (FileError InputDecodeError -> String
forall e. Error e => e -> String
displayError FileError InputDecodeError
err)

-- | Deserialise a verification key from text or a verification key file.
-- If a filepath is provided, the file can either be formatted as Bech32, hex,
-- or text envelope.
readVerificationKeyTextOrFileAnyOf
  :: VerificationKeyTextOrFile
  -> IO (Either VerificationKeyTextOrFileError SomeAddressVerificationKey)
readVerificationKeyTextOrFileAnyOf :: VerificationKeyTextOrFile
-> IO
     (Either VerificationKeyTextOrFileError SomeAddressVerificationKey)
readVerificationKeyTextOrFileAnyOf VerificationKeyTextOrFile
verKeyTextOrFile =
  case VerificationKeyTextOrFile
verKeyTextOrFile of
    VktofVerificationKeyText Text
vkText ->
      Either VerificationKeyTextOrFileError SomeAddressVerificationKey
-> IO
     (Either VerificationKeyTextOrFileError SomeAddressVerificationKey)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either VerificationKeyTextOrFileError SomeAddressVerificationKey
 -> IO
      (Either VerificationKeyTextOrFileError SomeAddressVerificationKey))
-> Either VerificationKeyTextOrFileError SomeAddressVerificationKey
-> IO
     (Either VerificationKeyTextOrFileError SomeAddressVerificationKey)
forall a b. (a -> b) -> a -> b
$ (InputDecodeError -> VerificationKeyTextOrFileError)
-> Either InputDecodeError SomeAddressVerificationKey
-> Either VerificationKeyTextOrFileError SomeAddressVerificationKey
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first InputDecodeError -> VerificationKeyTextOrFileError
VerificationKeyTextError (Either InputDecodeError SomeAddressVerificationKey
 -> Either
      VerificationKeyTextOrFileError SomeAddressVerificationKey)
-> Either InputDecodeError SomeAddressVerificationKey
-> Either VerificationKeyTextOrFileError SomeAddressVerificationKey
forall a b. (a -> b) -> a -> b
$
        ByteString -> Either InputDecodeError SomeAddressVerificationKey
deserialiseAnyVerificationKey (Text -> ByteString
Text.encodeUtf8 Text
vkText)
    VktofVerificationKeyFile (VerificationKeyFile String
fp) -> do
      ByteString
vkBs <- IO ByteString -> IO ByteString
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO ByteString -> IO ByteString) -> IO ByteString -> IO ByteString
forall a b. (a -> b) -> a -> b
$ String -> IO ByteString
BS.readFile String
fp
      Either VerificationKeyTextOrFileError SomeAddressVerificationKey
-> IO
     (Either VerificationKeyTextOrFileError SomeAddressVerificationKey)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either VerificationKeyTextOrFileError SomeAddressVerificationKey
 -> IO
      (Either VerificationKeyTextOrFileError SomeAddressVerificationKey))
-> Either VerificationKeyTextOrFileError SomeAddressVerificationKey
-> IO
     (Either VerificationKeyTextOrFileError SomeAddressVerificationKey)
forall a b. (a -> b) -> a -> b
$ (InputDecodeError -> VerificationKeyTextOrFileError)
-> Either InputDecodeError SomeAddressVerificationKey
-> Either VerificationKeyTextOrFileError SomeAddressVerificationKey
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first InputDecodeError -> VerificationKeyTextOrFileError
VerificationKeyTextError (Either InputDecodeError SomeAddressVerificationKey
 -> Either
      VerificationKeyTextOrFileError SomeAddressVerificationKey)
-> Either InputDecodeError SomeAddressVerificationKey
-> Either VerificationKeyTextOrFileError SomeAddressVerificationKey
forall a b. (a -> b) -> a -> b
$
        ByteString -> Either InputDecodeError SomeAddressVerificationKey
deserialiseAnyVerificationKey ByteString
vkBs


-- | Verification key, verification key hash, or path to a verification key
-- file.
data VerificationKeyOrHashOrFile keyrole
  = VerificationKeyOrFile !(VerificationKeyOrFile keyrole)
  -- ^ Either a verification key or path to a verification key file.
  | VerificationKeyHash !(Hash keyrole)
  -- ^ A verification key hash.

deriving instance (Show (VerificationKeyOrFile keyrole), Show (Hash keyrole))
  => Show (VerificationKeyOrHashOrFile keyrole)

deriving instance (Eq (VerificationKeyOrFile keyrole), Eq (Hash keyrole))
  => Eq (VerificationKeyOrHashOrFile keyrole)

-- | Read a verification key or verification key hash or verification key file
-- and return a verification key hash.
--
-- If a filepath is provided, the file can either be formatted as Bech32, hex,
-- or text envelope.
readVerificationKeyOrHashOrFile
  :: (Key keyrole, SerialiseAsBech32 (VerificationKey keyrole))
  => AsType keyrole
  -> VerificationKeyOrHashOrFile keyrole
  -> IO (Either (FileError InputDecodeError) (Hash keyrole))
readVerificationKeyOrHashOrFile :: AsType keyrole
-> VerificationKeyOrHashOrFile keyrole
-> IO (Either (FileError InputDecodeError) (Hash keyrole))
readVerificationKeyOrHashOrFile AsType keyrole
asType VerificationKeyOrHashOrFile keyrole
verKeyOrHashOrFile =
  case VerificationKeyOrHashOrFile keyrole
verKeyOrHashOrFile of
    VerificationKeyOrFile VerificationKeyOrFile keyrole
vkOrFile -> do
      Either (FileError InputDecodeError) (VerificationKey keyrole)
eitherVk <- AsType keyrole
-> VerificationKeyOrFile keyrole
-> IO
     (Either (FileError InputDecodeError) (VerificationKey keyrole))
forall keyrole.
(HasTextEnvelope (VerificationKey keyrole),
 SerialiseAsBech32 (VerificationKey keyrole)) =>
AsType keyrole
-> VerificationKeyOrFile keyrole
-> IO
     (Either (FileError InputDecodeError) (VerificationKey keyrole))
readVerificationKeyOrFile AsType keyrole
asType VerificationKeyOrFile keyrole
vkOrFile
      Either (FileError InputDecodeError) (Hash keyrole)
-> IO (Either (FileError InputDecodeError) (Hash keyrole))
forall (f :: * -> *) a. Applicative f => a -> f a
pure (VerificationKey keyrole -> Hash keyrole
forall keyrole.
Key keyrole =>
VerificationKey keyrole -> Hash keyrole
verificationKeyHash (VerificationKey keyrole -> Hash keyrole)
-> Either (FileError InputDecodeError) (VerificationKey keyrole)
-> Either (FileError InputDecodeError) (Hash keyrole)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Either (FileError InputDecodeError) (VerificationKey keyrole)
eitherVk)
    VerificationKeyHash Hash keyrole
vkHash -> Either (FileError InputDecodeError) (Hash keyrole)
-> IO (Either (FileError InputDecodeError) (Hash keyrole))
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Hash keyrole -> Either (FileError InputDecodeError) (Hash keyrole)
forall a b. b -> Either a b
Right Hash keyrole
vkHash)

-- | Read a verification key or verification key hash or verification key file
-- and return a verification key hash.
--
-- If a filepath is provided, it will be interpreted as a text envelope
-- formatted file.
readVerificationKeyOrHashOrTextEnvFile
  :: Key keyrole
  => AsType keyrole
  -> VerificationKeyOrHashOrFile keyrole
  -> IO (Either (FileError InputDecodeError) (Hash keyrole))
readVerificationKeyOrHashOrTextEnvFile :: AsType keyrole
-> VerificationKeyOrHashOrFile keyrole
-> IO (Either (FileError InputDecodeError) (Hash keyrole))
readVerificationKeyOrHashOrTextEnvFile AsType keyrole
asType VerificationKeyOrHashOrFile keyrole
verKeyOrHashOrFile =
  case VerificationKeyOrHashOrFile keyrole
verKeyOrHashOrFile of
    VerificationKeyOrFile VerificationKeyOrFile keyrole
vkOrFile -> do
      Either (FileError InputDecodeError) (VerificationKey keyrole)
eitherVk <- AsType keyrole
-> VerificationKeyOrFile keyrole
-> IO
     (Either (FileError InputDecodeError) (VerificationKey keyrole))
forall keyrole.
HasTextEnvelope (VerificationKey keyrole) =>
AsType keyrole
-> VerificationKeyOrFile keyrole
-> IO
     (Either (FileError InputDecodeError) (VerificationKey keyrole))
readVerificationKeyOrTextEnvFile AsType keyrole
asType VerificationKeyOrFile keyrole
vkOrFile
      Either (FileError InputDecodeError) (Hash keyrole)
-> IO (Either (FileError InputDecodeError) (Hash keyrole))
forall (f :: * -> *) a. Applicative f => a -> f a
pure (VerificationKey keyrole -> Hash keyrole
forall keyrole.
Key keyrole =>
VerificationKey keyrole -> Hash keyrole
verificationKeyHash (VerificationKey keyrole -> Hash keyrole)
-> Either (FileError InputDecodeError) (VerificationKey keyrole)
-> Either (FileError InputDecodeError) (Hash keyrole)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Either (FileError InputDecodeError) (VerificationKey keyrole)
eitherVk)
    VerificationKeyHash Hash keyrole
vkHash -> Either (FileError InputDecodeError) (Hash keyrole)
-> IO (Either (FileError InputDecodeError) (Hash keyrole))
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Hash keyrole -> Either (FileError InputDecodeError) (Hash keyrole)
forall a b. b -> Either a b
Right Hash keyrole
vkHash)

generateKeyPair :: Key keyrole => AsType keyrole -> IO (VerificationKey keyrole, SigningKey keyrole)
generateKeyPair :: AsType keyrole -> IO (VerificationKey keyrole, SigningKey keyrole)
generateKeyPair AsType keyrole
asType = do
  SigningKey keyrole
skey <- AsType keyrole -> IO (SigningKey keyrole)
forall keyrole.
Key keyrole =>
AsType keyrole -> IO (SigningKey keyrole)
generateSigningKey AsType keyrole
asType
  (VerificationKey keyrole, SigningKey keyrole)
-> IO (VerificationKey keyrole, SigningKey keyrole)
forall (m :: * -> *) a. Monad m => a -> m a
return (SigningKey keyrole -> VerificationKey keyrole
forall keyrole.
Key keyrole =>
SigningKey keyrole -> VerificationKey keyrole
getVerificationKey SigningKey keyrole
skey, SigningKey keyrole
skey)