{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications  #-}

module PlutusCore.Crypto.Ed25519 (
   verifyEd25519Signature_V1,
   verifyEd25519Signature_V2
   ) where

import PlutusCore.Builtin.KnownType (BuiltinResult)
import PlutusCore.Crypto.Utils

import Cardano.Crypto.DSIGN.Class qualified as DSIGN
import Cardano.Crypto.DSIGN.Ed25519 (Ed25519DSIGN)
import Crypto.ECC.Ed25519Donna (publicKey, signature, verify)
import Crypto.Error (CryptoFailable (..))
import Data.ByteString qualified as BS
import Data.Text (Text, pack)

-- | Ed25519 signature verification
-- This will fail if the key or the signature are not of the expected length.
-- This version uses the cardano-crypto implementation of the verification function.
verifyEd25519Signature_V1
    :: BS.ByteString  -- ^ Public Key (32 bytes)
    -> BS.ByteString  -- ^ Message    (arbitrary length)
    -> BS.ByteString  -- ^ Signature  (64 bytes)
    -> BuiltinResult Bool
verifyEd25519Signature_V1 :: ByteString -> ByteString -> ByteString -> BuiltinResult Bool
verifyEd25519Signature_V1 ByteString
pubKey ByteString
msg ByteString
sig =
    case PublicKey -> ByteString -> Signature -> Bool
forall ba.
ByteArrayAccess ba =>
PublicKey -> ba -> Signature -> Bool
verify
             (PublicKey -> ByteString -> Signature -> Bool)
-> CryptoFailable PublicKey
-> CryptoFailable (ByteString -> Signature -> Bool)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ByteString -> CryptoFailable PublicKey
forall ba. ByteArrayAccess ba => ba -> CryptoFailable PublicKey
publicKey ByteString
pubKey
             CryptoFailable (ByteString -> Signature -> Bool)
-> CryptoFailable ByteString -> CryptoFailable (Signature -> Bool)
forall a b.
CryptoFailable (a -> b) -> CryptoFailable a -> CryptoFailable b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> ByteString -> CryptoFailable ByteString
forall a. a -> CryptoFailable a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ByteString
msg
             CryptoFailable (Signature -> Bool)
-> CryptoFailable Signature -> CryptoFailable Bool
forall a b.
CryptoFailable (a -> b) -> CryptoFailable a -> CryptoFailable b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> ByteString -> CryptoFailable Signature
forall ba. ByteArrayAccess ba => ba -> CryptoFailable Signature
signature ByteString
sig
    of CryptoPassed Bool
r   -> Bool -> BuiltinResult Bool
forall a. a -> BuiltinResult a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
r
       CryptoFailed CryptoError
err -> Text -> Text -> BuiltinResult Bool
forall a. Text -> Text -> BuiltinResult a
failWithMessage Text
loc (Text -> BuiltinResult Bool) -> Text -> BuiltinResult Bool
forall a b. (a -> b) -> a -> b
$ String -> Text
pack (CryptoError -> String
forall a. Show a => a -> String
show CryptoError
err)
  where
    loc :: Text
    loc :: Text
loc = Text
"Ed25519 signature verification"

-- | Ed25519 signature verification
-- This will fail if the key or the signature are not of the expected length.
-- This version uses the cardano-crypto-class implementation of the verification
-- function (using libsodium).
verifyEd25519Signature_V2
    :: BS.ByteString  -- ^ Public Key (32 bytes)
    -> BS.ByteString  -- ^ Message    (arbitrary length)
    -> BS.ByteString  -- ^ Signature  (64 bytes)
    -> BuiltinResult Bool
verifyEd25519Signature_V2 :: ByteString -> ByteString -> ByteString -> BuiltinResult Bool
verifyEd25519Signature_V2 ByteString
pk ByteString
msg ByteString
sig =
  case forall v. DSIGNAlgorithm v => ByteString -> Maybe (VerKeyDSIGN v)
DSIGN.rawDeserialiseVerKeyDSIGN @Ed25519DSIGN ByteString
pk of
    Maybe (VerKeyDSIGN Ed25519DSIGN)
Nothing -> Text -> Text -> BuiltinResult Bool
forall a. Text -> Text -> BuiltinResult a
failWithMessage Text
loc Text
"Invalid verification key."
    Just VerKeyDSIGN Ed25519DSIGN
pk' -> case forall v. DSIGNAlgorithm v => ByteString -> Maybe (SigDSIGN v)
DSIGN.rawDeserialiseSigDSIGN @Ed25519DSIGN ByteString
sig of
      Maybe (SigDSIGN Ed25519DSIGN)
Nothing -> Text -> Text -> BuiltinResult Bool
forall a. Text -> Text -> BuiltinResult a
failWithMessage Text
loc Text
"Invalid signature."
      Just SigDSIGN Ed25519DSIGN
sig' ->
          Bool -> BuiltinResult Bool
forall a. a -> BuiltinResult a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool -> BuiltinResult Bool) -> Bool -> BuiltinResult Bool
forall a b. (a -> b) -> a -> b
$
               case ContextDSIGN Ed25519DSIGN
-> VerKeyDSIGN Ed25519DSIGN
-> ByteString
-> SigDSIGN Ed25519DSIGN
-> Either String ()
forall v a.
(DSIGNAlgorithm v, Signable v a, HasCallStack) =>
ContextDSIGN v
-> VerKeyDSIGN v -> a -> SigDSIGN v -> Either String ()
forall a.
(Signable Ed25519DSIGN a, HasCallStack) =>
ContextDSIGN Ed25519DSIGN
-> VerKeyDSIGN Ed25519DSIGN
-> a
-> SigDSIGN Ed25519DSIGN
-> Either String ()
DSIGN.verifyDSIGN () VerKeyDSIGN Ed25519DSIGN
pk' ByteString
msg SigDSIGN Ed25519DSIGN
sig' of
                 Left String
_   -> Bool
False
                 Right () -> Bool
True
  where
    loc :: Text
    loc :: Text
loc = Text
"Ed25519 signature verification"