diff --git a/core/orchestrator.go b/core/orchestrator.go index 85a05e074a..5aef600f7c 100644 --- a/core/orchestrator.go +++ b/core/orchestrator.go @@ -25,6 +25,7 @@ import ( "github.com/livepeer/go-livepeer/net" "github.com/livepeer/go-livepeer/pm" + lpcrypto "github.com/livepeer/go-livepeer/crypto" lpmon "github.com/livepeer/go-livepeer/monitor" ffmpeg "github.com/livepeer/lpms/ffmpeg" "github.com/livepeer/lpms/stream" @@ -67,7 +68,7 @@ func (orch *orchestrator) VerifySig(addr ethcommon.Address, msg string, sig []by if orch.node == nil || orch.node.Eth == nil { return true } - return pm.VerifySig(addr, crypto.Keccak256([]byte(msg)), sig) + return lpcrypto.VerifySig(addr, crypto.Keccak256([]byte(msg)), sig) } func (orch *orchestrator) Address() ethcommon.Address { diff --git a/crypto/verify.go b/crypto/verify.go new file mode 100644 index 0000000000..6595616c78 --- /dev/null +++ b/crypto/verify.go @@ -0,0 +1,56 @@ +package crypto + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +var ( + secp256k1N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16) + secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2)) +) + +// Verify verifies that a ETH ECDSA signature over a given message +// is produced by a given ETH address +func VerifySig(addr ethcommon.Address, msg, sig []byte) bool { + recovered, err := ecrecover(msg, sig) + if err != nil { + return false + } + + return recovered == addr +} + +func ecrecover(msg, sig []byte) (ethcommon.Address, error) { + if len(sig) != 65 { + return ethcommon.Address{}, errors.New("invalid signature length") + } + + s := new(big.Int).SetBytes(sig[32:64]) + if s.Cmp(secp256k1halfN) > 0 { + return ethcommon.Address{}, errors.New("signature s value too high") + } + + v := sig[64] + if v != byte(27) && v != byte(28) { + return ethcommon.Address{}, errors.New("signature v value must be 27 or 28") + } + + // crypto.SigToPub() expects signature v value = 0/1 + // Copy the signature and convert its value to 0/1 + ethSig := make([]byte, 65) + copy(ethSig[:], sig[:]) + ethSig[64] -= 27 + + ethMsg := accounts.TextHash(msg) + pubkey, err := crypto.SigToPub(ethMsg, ethSig) + if err != nil { + return ethcommon.Address{}, err + } + + return crypto.PubkeyToAddress(*pubkey), nil +} diff --git a/pm/helpers_test.go b/crypto/verify_test.go similarity index 99% rename from pm/helpers_test.go rename to crypto/verify_test.go index ecf8857698..ed5dd50235 100644 --- a/pm/helpers_test.go +++ b/crypto/verify_test.go @@ -1,4 +1,4 @@ -package pm +package crypto import ( "testing" diff --git a/eth/accountmanager_test.go b/eth/accountmanager_test.go index ffe1350ab0..bd397dd136 100644 --- a/eth/accountmanager_test.go +++ b/eth/accountmanager_test.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/core/types" - "github.com/livepeer/go-livepeer/pm" + "github.com/livepeer/go-livepeer/crypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -93,7 +93,7 @@ func TestSign(t *testing.T) { sig, err := am.Sign([]byte("foo")) assert.Nil(err) - assert.True(pm.VerifySig(a.Address, []byte("foo"), sig)) + assert.True(crypto.VerifySig(a.Address, []byte("foo"), sig)) } func tmpKeyStore(t *testing.T, encrypted bool) (string, *keystore.KeyStore) { diff --git a/pm/helpers.go b/pm/helpers.go index e95520fca6..de618c452f 100644 --- a/pm/helpers.go +++ b/pm/helpers.go @@ -1,63 +1,11 @@ package pm import ( - "math/big" "math/rand" - "github.com/ethereum/go-ethereum/accounts" ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/pkg/errors" ) -var ( - secp256k1N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16) - secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2)) -) - -// VerifySig verifies that a ETH ECDSA signature over a given message -// is produced by a given ETH address -// -// TODO refactor to a package that both eth and pm can import -func VerifySig(addr ethcommon.Address, msg, sig []byte) bool { - recovered, err := ecrecover(msg, sig) - if err != nil { - return false - } - - return recovered == addr -} - -func ecrecover(msg, sig []byte) (ethcommon.Address, error) { - if len(sig) != 65 { - return ethcommon.Address{}, errors.New("invalid signature length") - } - - s := new(big.Int).SetBytes(sig[32:64]) - if s.Cmp(secp256k1halfN) > 0 { - return ethcommon.Address{}, errors.New("signature s value too high") - } - - v := sig[64] - if v != byte(27) && v != byte(28) { - return ethcommon.Address{}, errors.New("signature v value must be 27 or 28") - } - - // crypto.SigToPub() expects signature v value = 0/1 - // Copy the signature and convert its value to 0/1 - ethSig := make([]byte, 65) - copy(ethSig[:], sig[:]) - ethSig[64] -= 27 - - ethMsg := accounts.TextHash(msg) - pubkey, err := crypto.SigToPub(ethMsg, ethSig) - if err != nil { - return ethcommon.Address{}, err - } - - return crypto.PubkeyToAddress(*pubkey), nil -} - // RandHash returns a random keccak256 hash func RandHash() ethcommon.Hash { return ethcommon.BytesToHash(RandBytes(32)) diff --git a/pm/sigverifier.go b/pm/sigverifier.go index 127db337df..ac6b2044d9 100644 --- a/pm/sigverifier.go +++ b/pm/sigverifier.go @@ -2,6 +2,7 @@ package pm import ( ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/livepeer/go-livepeer/crypto" ) // SigVerifier is an interface which describes an object capable @@ -20,7 +21,7 @@ type DefaultSigVerifier struct { // Verify checks if a provided signature over a message // is valid for a given ETH address func (sv *DefaultSigVerifier) Verify(addr ethcommon.Address, msg, sig []byte) bool { - return VerifySig(addr, msg, sig) + return crypto.VerifySig(addr, msg, sig) } // ApprovedSigVerifier is an implementation of the SigVerifier interface diff --git a/server/broadcast.go b/server/broadcast.go index f0b2dbc31e..7598ef6173 100644 --- a/server/broadcast.go +++ b/server/broadcast.go @@ -18,10 +18,10 @@ import ( "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" + lpcrypto "github.com/livepeer/go-livepeer/crypto" "github.com/livepeer/go-livepeer/drivers" "github.com/livepeer/go-livepeer/monitor" "github.com/livepeer/go-livepeer/net" - "github.com/livepeer/go-livepeer/pm" "github.com/livepeer/go-livepeer/verification" "github.com/livepeer/lpms/ffmpeg" @@ -491,7 +491,7 @@ func transcodeSegment(cxn *rtmpConnection, seg *stream.HLSSegment, name string, // Might not have seg hashes if results are directly uploaded to the broadcaster's OS // TODO: Consider downloading the results to generate seg hashes if results are directly uploaded to the broadcaster's OS len(segHashes) != len(res.Segments) && - !pm.VerifySig(ethcommon.BytesToAddress(ticketParams.Recipient), crypto.Keccak256(segHashes...), res.Sig) { + !lpcrypto.VerifySig(ethcommon.BytesToAddress(ticketParams.Recipient), crypto.Keccak256(segHashes...), res.Sig) { glog.Errorf("Sig check failed for segment nonce=%d seqNo=%d", nonce, seg.SeqNo) cxn.sessManager.removeSession(sess) return nil, errPMCheckFailed diff --git a/server/rpc_test.go b/server/rpc_test.go index d1a5a2d76f..5f22f829eb 100644 --- a/server/rpc_test.go +++ b/server/rpc_test.go @@ -23,6 +23,7 @@ import ( "github.com/livepeer/go-livepeer/common" "github.com/livepeer/go-livepeer/core" + "github.com/livepeer/go-livepeer/crypto" "github.com/livepeer/go-livepeer/drivers" "github.com/livepeer/go-livepeer/net" "github.com/livepeer/go-livepeer/pm" @@ -96,7 +97,7 @@ func (r *stubOrchestrator) Sign(msg []byte) ([]byte, error) { } func (r *stubOrchestrator) VerifySig(addr ethcommon.Address, msg string, sig []byte) bool { - return pm.VerifySig(addr, ethcrypto.Keccak256([]byte(msg)), sig) + return crypto.VerifySig(addr, ethcrypto.Keccak256([]byte(msg)), sig) } func (r *stubOrchestrator) Address() ethcommon.Address {