packages feed

hevm-0.50.4: src/EVM/Sign.hs

{-|
Module      : Helper functions to sign a transaction and derive address from
Description :        for the EVM given a secret key
-}

module EVM.Sign where

import qualified Crypto.Hash as Crypto
import Data.Maybe (fromMaybe)
import Crypto.PubKey.ECC.ECDSA (signDigestWith, PrivateKey(..), Signature(..))
import Crypto.PubKey.ECC.Types (getCurveByName, CurveName(..), Point(..))
import Crypto.PubKey.ECC.Generate (generateQ)

import EVM.ABI (encodeAbiValue, AbiValue (..))
import qualified Data.ByteString   as BS
import EVM.Types
import EVM.Expr (exprToAddr)
import EVM.Precompiled
import Data.Word


-- Given a secret key, generates the address
deriveAddr :: Integer -> Maybe Addr
deriveAddr sk = case pubPoint of
           PointO -> Nothing
           Point x y ->
             -- See yellow paper #286
               let pub = BS.concat [ encodeInt x, encodeInt y ]
                   addr = Lit . W256 . word256 . BS.drop 12 . BS.take 32 . keccakBytes $ pub
                in exprToAddr addr
         where
          curve = getCurveByName SEC_p256k1
          pubPoint = generateQ curve (num sk)
          encodeInt = encodeAbiValue . AbiUInt 256 . fromInteger

sign :: W256 -> Integer -> (Word8, W256, W256)
sign hash sk = (v, r, s)
  where
    -- setup curve params
    curve = getCurveByName SEC_p256k1
    priv = PrivateKey curve sk
    digest = fromMaybe
      (error $ "Internal Error: could produce a digest from " <> show hash)
      (Crypto.digestFromByteString (word256Bytes hash))

    -- sign message
    sig = ethsign priv digest
    r = num $ sign_r sig
    s = num lowS

    -- this is a little bit sad, but cryptonite doesn't give us back a v value
    -- so we compute it by guessing one, and then seeing if that gives us the right answer from ecrecover
    v = if ecrec 28 r s hash == deriveAddr sk
        then 28
        else 27

    -- we always use the lower S value to conform with EIP2 (re: ECDSA transaction malleability)
    -- https://eips.ethereum.org/EIPS/eip-2
    secpOrder = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 :: Integer
    lowS = if sign_s sig > secpOrder `div` 2
           then secpOrder - sign_s sig
           else sign_s sig

-- | We don't want to introduce the machinery needed to sign with a random nonce,
-- so we just use the same nonce every time (420). This is obviously very
-- insecure, but fine for testing purposes.
ethsign :: PrivateKey -> Crypto.Digest Crypto.Keccak_256 -> Signature
ethsign sk digest = go 420
  where
    go k = case signDigestWith k sk digest of
       Nothing  -> go (k + 1)
       Just sig -> sig

ecrec :: W256 -> W256 -> W256 -> W256 -> Maybe Addr
ecrec v r s e = num . word <$> EVM.Precompiled.execute 1 input 32
  where input = BS.concat (word256Bytes <$> [e, v, r, s])