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])