Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support v parameter for secp256k1 key recovery #21

Merged
merged 4 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 88 additions & 12 deletions src-secp256k1/Crypto/Secp256k1.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ module Crypto.Secp256k1
, ecdsaR
, EcdsaS
, ecdsaS
, EcdsaV(..)
, ecdsaV
, ecdsaVerify
, ecdsaRecoverPublicKey
) where
Expand Down Expand Up @@ -82,9 +84,67 @@ publicKeyPointFromBytes bs = do
-- Public API

newtype EcdsaPublicKey = EcdsaPublicKey Point
deriving (Show, Eq)

newtype EcdsaMessageDigest = EcdsaMessageDigest Fn
deriving (Show, Eq)

newtype EcdsaR = EcdsaR Fn
deriving (Show, Eq)

newtype EcdsaS = EcdsaS Fn
deriving (Show, Eq)

-- | The recovery id or V value of an ECDSA signature is used for public key
-- recovery. If present it indicates the parity of the y-coordinate of the
-- public key and whether the magnitude of the x-coordinate is lower than the curve order.
--
-- In Ethereum a value is originally computed as
-- \(27 + y-parity + (magnitude of x is lower curve order)\).
--
-- Since EIP-155 the value is computed for the Ethereum mainnet as
-- \(37 + y-parity + (magnitude of x is lower curve order)\).
--
-- Many external tools still use the original values and implementations
-- should therefore support both options.
--
-- Note, that the values 29, 30, 39, and 40 are extremly unlikely to occur with
-- randomly generate keys.
--
-- Also note, that the V value may not always be provided with the signature
-- in which case it is safe to just try all possible values when recovering
-- the public key.
--
data EcdsaV = EcdsaV
!Bool
-- ^ whether parity of the y-coordinate of public recovered public key
-- is odd.
--
-- If you don't know this parameter it is safe to try both options, at
-- the cost of taking on average 1.5 times more computation time to
-- compute the result.

!Bool
-- ^ whether the second solution for the public key is returned. This
-- parameter is almost surely always @False@.
--
-- If you don't know this value it is safe to assume that it is @False@.
--
-- https://www.secg.org/sec1-v2.pdf, 4.1.3:
--
-- The publicly verifiable criteria that r may be conditioned to satisfy may
-- include that xR is uniquely recoverable from r in that only one of the
-- integers \(xR = r + jn\) for \(j ∈ {0, 1, 2, ..., h}\) represents a valid
-- x-coordinate of a multiple of G. For the recommended curves [SEC 2] with h =
-- 1 and h = 2, the number of valid candidate x-coordinates is usually one, so
-- this is a vacuous check.
--
-- "Usually" here means something like always except for one out of \(2^{128}\).
-- However, in the context of a public blockchain, an attack may be able to
-- fabricate a respective signature and cause diverging behavior between
-- validating nodes with different implementations for handling this corner
-- case. Although, it is not clear whether creating such an attack is infeasible.
deriving (Show, Eq)

-- | Input: 65 bytes that represent an uncompressed (prefix 0x04) secp256k1
-- curve point.
Expand All @@ -95,6 +155,8 @@ ecdsaPublicKey = fmap EcdsaPublicKey . publicKeyPointFromBytes
-- | Returns 65 bytes that represent a public key encoded as uncompressed
-- secp256k1 curve point.
--
-- The encoding is @0x04 <> x <> y@.
--
ecdsaPublicKeyBytes :: EcdsaPublicKey -> BS.ShortByteString
ecdsaPublicKeyBytes (EcdsaPublicKey p) = pointToBytes p

Expand All @@ -120,6 +182,26 @@ ecdsaR = fmap (EcdsaR . shortBytesToFn) . checkLength "ecdsaR" 32
ecdsaS :: MonadThrow m => BS.ShortByteString -> m EcdsaS
ecdsaS = fmap (EcdsaS . shortBytesToFn) . checkLength "ecdsaS" 32

-- | Input: 1 byte long V value of the secp256k1 ECDSA signature
--
ecdsaV :: MonadThrow m => BS.ShortByteString -> m EcdsaV
ecdsaV = checkLength "ecdsaV" 1 >=> \case

-- before EIP-155
"\27" -> return $ EcdsaV False False
"\28" -> return $ EcdsaV True False
"\29" -> return $ EcdsaV False True
"\30" -> return $ EcdsaV True True

-- EIP-155
"\37" -> return $ EcdsaV False False
"\38" -> return $ EcdsaV True False
"\39" -> return $ EcdsaV False True
"\40" -> return $ EcdsaV True True

e -> throwM $ EcdsaException $
"Invalid V value for signature: " <> sshow e

ecdsaVerify
:: MonadThrow m
=> EcdsaMessageDigest
Expand All @@ -146,19 +228,13 @@ ecdsaRecoverPublicKey
-- ^ The R value of the input signature
-> EcdsaS
-- ^ The S value of the input singature
-> Bool
-- ^ whether parity of the public recovered public key is odd.
--
-- If you don't know this parameter it is safe to try both options, at
-- the cost of taking on average 1.5 times more computation time to
-- compute the result.
-> Bool
-- ^ whether the second solution for the public key is returned. This
-- parameter is almost surely always @False@.
-> EcdsaV
-- ^ The recovery id or V value of the input singature
--
-- If you don't know this value it is safe to assume that it is @False@.
-- If you don't know this value you may just try all 4 possible value,
-- where the values with 'ecdsaVHigh' are extremely unlikely.
--
-> Maybe EcdsaPublicKey
ecdsaRecoverPublicKey (EcdsaMessageDigest d) (EcdsaR r) (EcdsaS s) oddY secondKey =
EcdsaPublicKey <$> recoverPublicKey d r s oddY secondKey
ecdsaRecoverPublicKey (EcdsaMessageDigest d) (EcdsaR r) (EcdsaS s) (EcdsaV isOddY isSecond) =
EcdsaPublicKey <$> recoverPublicKey d r s isOddY isSecond

14 changes: 9 additions & 5 deletions src-secp256k1/Crypto/Secp256k1/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -666,13 +666,17 @@ recoverPublicKey
-> Bool
-- ^ is second key
-> Maybe Point
recoverPublicKey e r s oddY secondKey = case nC *. rP of
O -> if validatePublicKey pk then Just pk else Nothing
_ -> error "something went wrong (probably the value for second key is incorrect)"
recoverPublicKey e r s oddY secondKey
| Just rP <- pointFromX x oddY =
case nC *. rP of
O ->
let pk = invM r .*. (s .*. rP .-. e .*. gC)
in if validatePublicKey pk then Just pk else Nothing
_ -> error "something went wrong (probably the value for second key is incorrect)"
| otherwise = Nothing
where
x = if secondKey then zConv r .+ fp nC else zConv r
Just rP = pointFromX x oddY -- FIXME this fails for an invalid @secondKey@ value
pk = invM r .*. (s .*. rP .-. e .*. gC)


-- -------------------------------------------------------------------------- --
-- Hexdecimal Representation
Expand Down
113 changes: 108 additions & 5 deletions test-secp256k1/Test/Crypto/Secp256k1/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import qualified Data.ByteString.Short as BS
import Data.Coerce
import Data.Hash.SHA3
import Data.Word (Word8)

import GHC.TypeNats

Expand All @@ -40,13 +41,14 @@
-- internal modules

import Crypto.Secp256k1.Internal
import Crypto.Secp256k1

-- -------------------------------------------------------------------------- --
-- Examples
-- -------------------------------------------------------------------------- --

sk1, h1, r1, s1 :: Fn
sk1 = fn 0xebb2c082fd7727890a28ac82f6bdf97bad8de9f5d7c9028692de1a255cad3e0f

Check warning on line 51 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, ubuntu-20.04)

Defined but not used: ‘sk1’

Check warning on line 51 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, ubuntu-22.04)

Defined but not used: ‘sk1’

Check warning on line 51 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, macOS-latest)

Defined but not used: ‘sk1’

Check warning on line 51 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, ubuntu-20.04)

Defined but not used: ‘sk1’

Check warning on line 51 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, ubuntu-22.04)

Defined but not used: ‘sk1’

Check warning on line 51 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, macOS-latest)

Defined but not used: ‘sk1’
h1 = fn 0x4b688df40bcedbe641ddb16ff0a1842d9c67ea1c3bf63f3e0471baa664531d1a
r1 = fn 0x241097efbf8b63bf145c8961dbdf10c310efbb3b2676bbc0f8b08505c9e2f795
s1 = fn 0x021006b7838609339e8b415a7f9acb1b661828131aef1ecbc7955dfb01f3ca0e
Expand All @@ -64,18 +66,22 @@
test_1_recover :: Property
test_1_recover = recoverPublicKey h1 r1 s1 False False === Just pk1

test_2_recover :: Property
test_2_recover = recoverPublicKey h1 r1 s1 True False =/= Just pk1

properties_example1 :: TestTree
properties_example1 = testGroup "example1"
[ testProperty "test_1_verify" test_1_verify
, testProperty "test_1_recover" test_1_recover
, testProperty "test_2_recover" test_2_recover
]

sk2, k2, h2, r2, s2 :: Fn
sk2 = fn 0xebb2c082fd7727890a28ac82f6bdf97bad8de9f5d7c9028692de1a255cad3e0f

Check warning on line 80 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, ubuntu-20.04)

Defined but not used: ‘sk2’

Check warning on line 80 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, ubuntu-22.04)

Defined but not used: ‘sk2’

Check warning on line 80 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, macOS-latest)

Defined but not used: ‘sk2’

Check warning on line 80 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, ubuntu-20.04)

Defined but not used: ‘sk2’

Check warning on line 80 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, ubuntu-22.04)

Defined but not used: ‘sk2’

Check warning on line 80 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, macOS-latest)

Defined but not used: ‘sk2’
k2 = fn 0x49a0d7b786ec9cde0d0721d72804befd06571c974b191efb42ecf322ba9ddd9a

Check warning on line 81 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, ubuntu-20.04)

Defined but not used: ‘k2’

Check warning on line 81 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, ubuntu-22.04)

Defined but not used: ‘k2’

Check warning on line 81 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, macOS-latest)

Defined but not used: ‘k2’

Check warning on line 81 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, ubuntu-20.04)

Defined but not used: ‘k2’

Check warning on line 81 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, ubuntu-22.04)

Defined but not used: ‘k2’

Check warning on line 81 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, macOS-latest)

Defined but not used: ‘k2’
h2 = fn 0x4b688df40bcedbe641ddb16ff0a1842d9c67ea1c3bf63f3e0471baa664531d1a

Check warning on line 82 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, ubuntu-20.04)

Defined but not used: ‘h2’

Check warning on line 82 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, ubuntu-22.04)

Defined but not used: ‘h2’

Check warning on line 82 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, macOS-latest)

Defined but not used: ‘h2’

Check warning on line 82 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, ubuntu-20.04)

Defined but not used: ‘h2’

Check warning on line 82 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, ubuntu-22.04)

Defined but not used: ‘h2’

Check warning on line 82 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, macOS-latest)

Defined but not used: ‘h2’
r2 = fn 0x241097efbf8b63bf145c8961dbdf10c310efbb3b2676bbc0f8b08505c9e2f795

Check warning on line 83 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, ubuntu-20.04)

Defined but not used: ‘r2’

Check warning on line 83 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, ubuntu-22.04)

Defined but not used: ‘r2’

Check warning on line 83 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, macOS-latest)

Defined but not used: ‘r2’

Check warning on line 83 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, ubuntu-20.04)

Defined but not used: ‘r2’

Check warning on line 83 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, ubuntu-22.04)

Defined but not used: ‘r2’

Check warning on line 83 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, macOS-latest)

Defined but not used: ‘r2’
s2 = fn 0x021006b7838609339e8b415a7f9acb1b661828131aef1ecbc7955dfb01f3ca0e

Check warning on line 84 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, ubuntu-20.04)

Defined but not used: ‘s2’

Check warning on line 84 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, ubuntu-22.04)

Defined but not used: ‘s2’

Check warning on line 84 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, macOS-latest)

Defined but not used: ‘s2’

Check warning on line 84 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, ubuntu-20.04)

Defined but not used: ‘s2’

Check warning on line 84 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, ubuntu-22.04)

Defined but not used: ‘s2’

Check warning on line 84 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, macOS-latest)

Defined but not used: ‘s2’

-- -------------------------------------------------------------------------- --
-- Tests Tools
Expand Down Expand Up @@ -124,7 +130,7 @@
-- ^ (r, s, isOddY, isSecondKey)
sign sk e = do
k <- genSecretKey
let (Point xr yr) = getPublicKey k

Check warning on line 133 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, ubuntu-20.04)

Pattern match(es) are non-exhaustive

Check warning on line 133 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, ubuntu-22.04)

Pattern match(es) are non-exhaustive

Check warning on line 133 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.2, 3.10, macOS-latest)

Pattern match(es) are non-exhaustive

Check warning on line 133 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, ubuntu-20.04)

Pattern match(es) are non-exhaustive

Check warning on line 133 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, ubuntu-22.04)

Pattern match(es) are non-exhaustive

Check warning on line 133 in test-secp256k1/Test/Crypto/Secp256k1/Internal.hs

View workflow job for this annotation

GitHub Actions / Build (9.6, 3.10, macOS-latest)

Pattern match(es) are non-exhaustive
r = zConv xr
if r == fn 0
then sign sk e -- start over
Expand All @@ -150,8 +156,8 @@
-- -------------------------------------------------------------------------- --
-- ECDSA Properties

prop_ecdsa_verify :: Int -> Property
prop_ecdsa_verify msg = ioProperty $ do
prop_verify :: Int -> Property
prop_verify msg = ioProperty $ do
(sk, pk) <- genKey
(r, s, isOddY, isSecondKey) <- sign sk msgDigest
return
Expand All @@ -161,8 +167,8 @@
where
msgDigest = hashMsg @Sha3_256 $ B8.pack $ show msg

prop_ecdsa_recover :: Int -> Property
prop_ecdsa_recover msg = ioProperty $ do
prop_recover :: Int -> Property
prop_recover msg = ioProperty $ do
(sk, pk) <- genKey
(r, s, isOddY, isSecondKey) <- sign sk msgDigest
return
Expand All @@ -180,8 +186,104 @@

properties_ecdsa :: TestTree
properties_ecdsa = testGroup "ECDSA"
[ testProperty "prop_verify" prop_verify
, testProperty "prop_recover" prop_recover
]

-- -------------------------------------------------------------------------- --
-- Public ECDSA API

ecdsaGenKey :: IO (Fn, EcdsaPublicKey)
ecdsaGenKey = do
k <- genKey
traverse (ecdsaPublicKey . pointToBytes) k
where
pointToBytes :: Point -> BS.ShortByteString
pointToBytes (Point x y) = BS.cons 0x04 (fpToShortBytes x <> fpToShortBytes y)
pointToBytes O = BS.pack [0x00]

ecdsaSign_
:: Word8
-> Fn
-> B.ByteString
-> IO (EcdsaR, EcdsaS, EcdsaV)
ecdsaSign_ x sk msg = do
(r, s, isOddY, isSecondKey) <- sign sk (hashMsg @Sha3_256 msg)
(,,)
<$> ecdsaR (fnToShortBytes r)
<*> ecdsaS (fnToShortBytes s)
<*> ecdsaV (BS.singleton (x + if isOddY then 1 else 0 + if isSecondKey then 1 else 0))

ecdsaSignEip155
:: Fn
-> B.ByteString
-> IO (EcdsaR, EcdsaS, EcdsaV)
ecdsaSignEip155 = ecdsaSign_ 37

ecdsaSignOrig
:: Fn
-> B.ByteString
-> IO (EcdsaR, EcdsaS, EcdsaV)
ecdsaSignOrig = ecdsaSign_ 27

ecdsaHashMsg
:: forall h
. Hash h
=> Coercible h BS.ShortByteString
=> B.ByteString
-> EcdsaMessageDigest
ecdsaHashMsg msg = case ecdsaMessageDigest h of
Left e -> error (show e)
Right d -> d
where
h = coerce (hashByteString @h msg)

prop_ecdsa_verify :: Int -> Property
prop_ecdsa_verify msg = ioProperty $ do
(sk, pk) <- ecdsaGenKey
(r, s, _v) <- ecdsaSignEip155 sk msgBytes
return $ case ecdsaVerify msgDigest pk r s of
Right x -> x === True
Left e -> counterexample (show e) $ False
where
msgBytes = B8.pack $ show msg
msgDigest = ecdsaHashMsg @Sha3_256 msgBytes

prop_ecdsa_recover_orig :: Int -> Property
prop_ecdsa_recover_orig msg = ioProperty $ do
(sk, pk) <- ecdsaGenKey
(r, s, v) <- ecdsaSignOrig sk msgBytes
return
$ counterexample ("sk: " <> show sk)
$ counterexample ("msgDigest: " <> show msgDigest)
$ counterexample ("r: " <> show r)
$ counterexample ("s: " <> show s)
$ counterexample ("v: " <> show v)
$ ecdsaRecoverPublicKey msgDigest r s v === Just pk
where
msgBytes = B8.pack $ show msg
msgDigest = ecdsaHashMsg @Sha3_256 msgBytes

prop_ecdsa_recover_eip155 :: Int -> Property
prop_ecdsa_recover_eip155 msg = ioProperty $ do
(sk, pk) <- ecdsaGenKey
(r, s, v) <- ecdsaSignEip155 sk msgBytes
return
$ counterexample ("sk: " <> show sk)
$ counterexample ("msgDigest: " <> show msgDigest)
$ counterexample ("r: " <> show r)
$ counterexample ("s: " <> show s)
$ counterexample ("v: " <> show v)
$ ecdsaRecoverPublicKey msgDigest r s v === Just pk
where
msgBytes = B8.pack $ show msg
msgDigest = ecdsaHashMsg @Sha3_256 msgBytes

properties_ecdsa_api :: TestTree
properties_ecdsa_api = testGroup "ECDSA"
[ testProperty "prop_ecdsa_verify" prop_ecdsa_verify
, testProperty "prop_ecdsa_recover" prop_ecdsa_recover
, testProperty "prop_ecdsa_recover_orig" prop_ecdsa_recover_orig
, testProperty "prop_ecdsa_recover_eip155" prop_ecdsa_recover_eip155
]

-- -------------------------------------------------------------------------- --
Expand Down Expand Up @@ -363,5 +465,6 @@
, properties_Fp_sqrt
, properties_P
, properties_ecdsa
, properties_ecdsa_api
, properties_example1
]
Loading