Skip to content
This repository has been archived by the owner on May 13, 2022. It is now read-only.

Reintroducing ecrecover and implementing ed25519 signature verification #777

Closed
wants to merge 1 commit into from
Closed
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
11 changes: 11 additions & 0 deletions account/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"io"

"github.com/hyperledger/burrow/secp256k1"
"github.com/tendermint/go-crypto"
"golang.org/x/crypto/ed25519"
)
Expand Down Expand Up @@ -47,6 +48,16 @@ func PublicKeyFromBytes(bs []byte) (PublicKey, error) {
return PublicKeyFromGoCryptoPubKey(pubKeyEd25519.Wrap())
}

// EcRecover returns the uncompressed public key that created the given signature.
func EcRecover(hash, sig []byte) ([]byte, error) {
return secp256k1.RecoverPubkey(hash, sig)
}

// EdVerify reports whether sig is valid signature of message by public key
func EdVerify(publicKey ed25519.PublicKey, message, sig []byte) bool {
return ed25519.Verify(publicKey, message, sig)
}

// Returns a copy of the raw untyped public key bytes
func (pk PublicKey) RawBytes() []byte {
switch pubKey := pk.PubKey.Unwrap().(type) {
Expand Down
19 changes: 19 additions & 0 deletions account/crypto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@ import (
"bytes"
"testing"

"github.com/hyperledger/burrow/util/hexutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// From go-ethereum/crypto/signature_test.go
var (
testmsg = hexutil.MustDecode("0xce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008")
testsig = hexutil.MustDecode("0x90f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549984a691139ad57a3f0b906637673aa2f63d1f55cb1a69199d4009eea23ceaddc9301")
testpubkey = hexutil.MustDecode("0x04e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652")
)

func TestGeneratePrivateKey(t *testing.T) {
privateKey, err := GeneratePrivateKey(bytes.NewBuffer([]byte{
1, 2, 3, 4, 5, 6, 7, 8,
Expand All @@ -26,3 +34,14 @@ func TestGeneratePrivateKey(t *testing.T) {
goodKey[31] = 2
assert.Error(t, EnsureEd25519PrivateKeyCorrect(badKey))
}

// From go-ethereum/crypto/signature_test.go
func TestEcrecover(t *testing.T) {
pubkey, err := EcRecover(testmsg, testsig)
if err != nil {
t.Fatalf("recover error: %s", err)
}
if !bytes.Equal(pubkey, testpubkey) {
t.Errorf("pubkey mismatch: want: %x have: %x", testpubkey, pubkey)
}
}
38 changes: 38 additions & 0 deletions binary/byteslice.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@

package binary

import "math/big"

// From go-ethereum/common/math/big.go
const (
// number of bits in a big.Word
wordBits = 32 << (uint64(^big.Word(0)) >> 63)
// number of bytes in a big.Word
wordBytes = wordBits / 8
)

func Fingerprint(slice []byte) []byte {
fingerprint := make([]byte, 6)
copy(fingerprint, slice)
Expand Down Expand Up @@ -46,3 +56,31 @@ func LeftPadBytes(slice []byte, l int) []byte {
copy(padded[l-len(slice):], slice)
return padded
}

// ReadBits encodes the absolute value of bigint as big-endian bytes. Callers must ensure that buf has enough space.
// If buf is too short the result will be incomplete.
// From go-ethereum/common/math/big.go
func ReadBits(bigint *big.Int, buf []byte) {
i := len(buf)
for _, d := range bigint.Bits() {
for j := 0; j < wordBytes && i > 0; j++ {
i--
buf[i] = byte(d)
d >>= 8
}
}
}

// GetData returns a slice from the data based on the start and size and pads up to size with zero's and is overflow safe.
// From go-ethereum/core/vm/common.go
func GetData(data []byte, start uint64, size uint64) []byte {
length := uint64(len(data))
if start > length {
start = length
}
end := start + size
if end > length {
end = length
}
return RightPadBytes(data[start:end], int(size))
}
1 change: 1 addition & 0 deletions execution/evm/gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const (
GasStackOp uint64 = 1

GasEcRecover uint64 = 1
GasEdVerify uint64 = 1
GasSha256Word uint64 = 1
GasSha256Base uint64 = 1
GasRipemd160Word uint64 = 1
Expand Down
41 changes: 35 additions & 6 deletions execution/evm/native.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,16 @@ import (
acm "github.com/hyperledger/burrow/account"
"github.com/hyperledger/burrow/account/state"
. "github.com/hyperledger/burrow/binary"
"github.com/hyperledger/burrow/execution/evm/sha3"
"github.com/hyperledger/burrow/logging"
"github.com/hyperledger/burrow/secp256k1"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/ripemd160"
)

var registeredNativeContracts = make(map[Word256]NativeContract)
var true32Byte = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
var false32Byte = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

func RegisteredNativeContract(address Word256) bool {
_, ok := registeredNativeContracts[address]
Expand All @@ -46,40 +51,64 @@ func init() {
}

func registerNativeContracts() {
// registeredNativeContracts[Int64ToWord256(1)] = ecrecoverFunc
registeredNativeContracts[Int64ToWord256(1)] = ecrecoverFunc
registeredNativeContracts[Int64ToWord256(2)] = sha256Func
registeredNativeContracts[Int64ToWord256(3)] = ripemd160Func
registeredNativeContracts[Int64ToWord256(4)] = identityFunc
registeredNativeContracts[Int64ToWord256(5)] = edverifyFunc
}

//-----------------------------------------------------------------------------

type NativeContract func(state state.Writer, caller acm.Account, input []byte, gas *uint64,
logger *logging.Logger) (output []byte, err error)

/* Removed due to C dependency
func ecrecoverFunc(state State, caller *acm.Account, input []byte, gas *int64) (output []byte, err error) {
func ecrecoverFunc(state state.Writer, caller acm.Account, input []byte, gas *uint64,
logger *logging.Logger) (output []byte, err error) {
// Deduct gas
gasRequired := GasEcRecover
if *gas < gasRequired {
return nil, ErrInsufficientGas
} else {
*gas -= gasRequired
}
// Recover

// SECP256K1 Recovery
hash := input[:32]
v := byte(input[32] - 27) // ignore input[33:64], v is small.
sig := append(input[64:], v)

recovered, err := secp256k1.RecoverPubkey(hash, sig)
if err != nil {
return nil, err
OH NO STOCASTIC CAT CODING!!!!
}
hashed := sha3.Sha3(recovered[1:])
return LeftPadBytes(hashed, 32), nil
}
*/

func edverifyFunc(state state.Writer, caller acm.Account, input []byte, gas *uint64,
logger *logging.Logger) (output []byte, err error) {
// Deduct gas
gasRequired := GasEdVerify
if *gas < gasRequired {
return nil, ErrInsufficientGas
} else {
*gas -= gasRequired
}

// ED25519 Sig Verify
// https://github.com/ethereum/go-ethereum/pull/16453/files
message := GetData(input, 0, 32)
publicKey := GetData(input, 32, 32)
edsig := GetData(input, 64, 64)

// Verify the Ed25519 signature against the public key and message and return result
// https://godoc.org/golang.org/x/crypto/ed25519#Verify
if ed25519.Verify(publicKey, message, edsig) {
return true32Byte, nil
}
return false32Byte, nil
}

func sha256Func(state state.Writer, caller acm.Account, input []byte, gas *uint64,
logger *logging.Logger) (output []byte, err error) {
Expand Down
Loading