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

module PlutusCore.Crypto.Secp256k1 (
   verifyEcdsaSecp256k1Signature,
   verifySchnorrSecp256k1Signature
   ) where

import PlutusCore.Builtin.Result
import PlutusCore.Crypto.Utils

import Cardano.Crypto.DSIGN.Class qualified as DSIGN
import Cardano.Crypto.DSIGN.EcdsaSecp256k1 (EcdsaSecp256k1DSIGN, toMessageHash)
import Cardano.Crypto.DSIGN.SchnorrSecp256k1 (SchnorrSecp256k1DSIGN)
import Data.ByteString qualified as BS
import Data.Text (Text)

-- | Verify an ECDSA signature made using the SECP256k1 curve.
--
-- = Note
--
-- There are additional well-formation requirements for the arguments beyond
-- their length:
--
-- * The first byte of the public key must correspond to the sign of the /y/
-- coordinate: this is @0x02@ if /y/ is even, and @0x03@ otherwise.
-- * The remaining bytes of the public key must correspond to the /x/
-- coordinate, as a big-endian integer.
-- * The first 32 bytes of the signature must correspond to the big-endian
-- integer representation of _r_.
-- * The last 32 bytes of the signature must correspond to the big-endian
-- integer representation of _s_.
--
-- While this primitive /accepts/ a hash, any caller should only pass it hashes
-- that they computed themselves: specifically, they should receive the
-- /message/ from a sender and hash it, rather than receiving the /hash/ from
-- said sender. Failure to do so can be
-- [dangerous](https://bitcoin.stackexchange.com/a/81116/35586). Other than
-- length, we make no requirements of what hash gets used.
verifyEcdsaSecp256k1Signature
  :: BS.ByteString -- ^ Public key   (33 bytes)
  -> BS.ByteString -- ^ Message hash (32 bytes)
  -> BS.ByteString -- ^ Signature    (64 bytes)
  -> BuiltinResult Bool
verifyEcdsaSecp256k1Signature :: ByteString -> ByteString -> ByteString -> BuiltinResult Bool
verifyEcdsaSecp256k1Signature ByteString
pk ByteString
msg ByteString
sig =
  case forall v. DSIGNAlgorithm v => ByteString -> Maybe (VerKeyDSIGN v)
DSIGN.rawDeserialiseVerKeyDSIGN @EcdsaSecp256k1DSIGN ByteString
pk of
    Maybe (VerKeyDSIGN EcdsaSecp256k1DSIGN)
Nothing -> Text -> Text -> BuiltinResult Bool
forall a. Text -> Text -> BuiltinResult a
failWithMessage Text
loc Text
"Invalid verification key."
    Just VerKeyDSIGN EcdsaSecp256k1DSIGN
pk' -> case forall v. DSIGNAlgorithm v => ByteString -> Maybe (SigDSIGN v)
DSIGN.rawDeserialiseSigDSIGN @EcdsaSecp256k1DSIGN ByteString
sig of
      Maybe (SigDSIGN EcdsaSecp256k1DSIGN)
Nothing -> Text -> Text -> BuiltinResult Bool
forall a. Text -> Text -> BuiltinResult a
failWithMessage Text
loc Text
"Invalid signature."
      Just SigDSIGN EcdsaSecp256k1DSIGN
sig' -> case ByteString -> Maybe MessageHash
toMessageHash ByteString
msg of
        Maybe MessageHash
Nothing -> Text -> Text -> BuiltinResult Bool
forall a. Text -> Text -> BuiltinResult a
failWithMessage Text
loc Text
"Invalid message hash."
        Just MessageHash
msg' -> 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 EcdsaSecp256k1DSIGN
-> VerKeyDSIGN EcdsaSecp256k1DSIGN
-> MessageHash
-> SigDSIGN EcdsaSecp256k1DSIGN
-> Either String ()
forall v a.
(DSIGNAlgorithm v, Signable v a, HasCallStack) =>
ContextDSIGN v
-> VerKeyDSIGN v -> a -> SigDSIGN v -> Either String ()
forall a.
(Signable EcdsaSecp256k1DSIGN a, HasCallStack) =>
ContextDSIGN EcdsaSecp256k1DSIGN
-> VerKeyDSIGN EcdsaSecp256k1DSIGN
-> a
-> SigDSIGN EcdsaSecp256k1DSIGN
-> Either String ()
DSIGN.verifyDSIGN () VerKeyDSIGN EcdsaSecp256k1DSIGN
pk' MessageHash
msg' SigDSIGN EcdsaSecp256k1DSIGN
sig' of
          Left String
_   -> Bool
False
          Right () -> Bool
True
  where
    loc :: Text
    loc :: Text
loc = Text
"ECDSA SECP256k1 signature verification"

-- | Verify a Schnorr signature made using the SECP256k1 curve.
--
-- = Note
--
-- There are additional well-formation requirements for the arguments beyond
-- their length. Throughout, we refer to co-ordinates of the point @R@.
--
-- * The bytes of the public key must correspond to the /x/ coordinate, as a
-- big-endian integer, as specified in BIP-340.
-- * The first 32 bytes of the signature must correspond to the /x/ coordinate,
-- as a big-endian integer, as specified in BIP-340.
-- * The last 32 bytes of the signature must correspond to the bytes of /s/, as
-- a big-endian integer, as specified in BIP-340.
--
-- = See also
--
-- * [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki)
verifySchnorrSecp256k1Signature
  :: BS.ByteString -- ^ Public key (32 bytes)
  -> BS.ByteString -- ^ Message    (arbitrary length)
  -> BS.ByteString -- ^ Signature  (64 bytes)
  -> BuiltinResult Bool
verifySchnorrSecp256k1Signature :: ByteString -> ByteString -> ByteString -> BuiltinResult Bool
verifySchnorrSecp256k1Signature ByteString
pk ByteString
msg ByteString
sig =
  case forall v. DSIGNAlgorithm v => ByteString -> Maybe (VerKeyDSIGN v)
DSIGN.rawDeserialiseVerKeyDSIGN @SchnorrSecp256k1DSIGN ByteString
pk of
    Maybe (VerKeyDSIGN SchnorrSecp256k1DSIGN)
Nothing -> Text -> Text -> BuiltinResult Bool
forall a. Text -> Text -> BuiltinResult a
failWithMessage Text
loc Text
"Invalid verification key."
    Just VerKeyDSIGN SchnorrSecp256k1DSIGN
pk' -> case forall v. DSIGNAlgorithm v => ByteString -> Maybe (SigDSIGN v)
DSIGN.rawDeserialiseSigDSIGN @SchnorrSecp256k1DSIGN ByteString
sig of
      Maybe (SigDSIGN SchnorrSecp256k1DSIGN)
Nothing -> Text -> Text -> BuiltinResult Bool
forall a. Text -> Text -> BuiltinResult a
failWithMessage Text
loc Text
"Invalid signature."
      Just SigDSIGN SchnorrSecp256k1DSIGN
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 SchnorrSecp256k1DSIGN
-> VerKeyDSIGN SchnorrSecp256k1DSIGN
-> ByteString
-> SigDSIGN SchnorrSecp256k1DSIGN
-> Either String ()
forall v a.
(DSIGNAlgorithm v, Signable v a, HasCallStack) =>
ContextDSIGN v
-> VerKeyDSIGN v -> a -> SigDSIGN v -> Either String ()
forall a.
(Signable SchnorrSecp256k1DSIGN a, HasCallStack) =>
ContextDSIGN SchnorrSecp256k1DSIGN
-> VerKeyDSIGN SchnorrSecp256k1DSIGN
-> a
-> SigDSIGN SchnorrSecp256k1DSIGN
-> Either String ()
DSIGN.verifyDSIGN () VerKeyDSIGN SchnorrSecp256k1DSIGN
pk' ByteString
msg SigDSIGN SchnorrSecp256k1DSIGN
sig' of
        Left String
_   -> Bool
False
        Right () -> Bool
True
  where
    loc :: Text
    loc :: Text
loc = Text
"Schnorr SECP256k1 signature verification"