From cd854798234de583757b08e133e5b2e88879ba52 Mon Sep 17 00:00:00 2001 From: simlecode <69969590+simlecode@users.noreply.github.com> Date: Fri, 16 Dec 2022 17:12:13 +0800 Subject: [PATCH] feat: Support creation and signing of Eth keys --- pkg/crypto/crypto.go | 13 +++++++++ pkg/crypto/crypto_test.go | 32 +++++++++++++++++++++++ pkg/crypto/delegated/init.go | 10 ++++++- pkg/crypto/keyinfo.go | 18 ++++++++++++- pkg/gen/genesis/fevm.go | 6 ++--- pkg/wallet/dsbackend.go | 16 +++++++++++- pkg/wallet/wallet_test.go | 45 ++++++++++++++++++++++++++++++++ venus-shared/types/eth.go | 2 +- venus-shared/utils/method_map.go | 2 +- 9 files changed, 136 insertions(+), 8 deletions(-) diff --git a/pkg/crypto/crypto.go b/pkg/crypto/crypto.go index b896cc0010..48cbdbad8d 100644 --- a/pkg/crypto/crypto.go +++ b/pkg/crypto/crypto.go @@ -36,3 +36,16 @@ func NewBLSKeyFromSeed(seed io.Reader) (KeyInfo, error) { copy(k, make([]byte, len(k))) // wipe with zero bytes return *ki, nil } + +func NewDelegatedKeyFromSeed(seed io.Reader) (KeyInfo, error) { + k, err := sigs[crypto.SigTypeDelegated].GenPrivateFromSeed(seed) + if err != nil { + return KeyInfo{}, err + } + ki := &KeyInfo{ + SigType: SigTypeDelegated, + } + ki.SetPrivateKey(k) + copy(k, make([]byte, len(k))) // wipe with zero bytes + return *ki, nil +} diff --git a/pkg/crypto/crypto_test.go b/pkg/crypto/crypto_test.go index 76e6136b7c..c59a3d311a 100644 --- a/pkg/crypto/crypto_test.go +++ b/pkg/crypto/crypto_test.go @@ -16,6 +16,7 @@ import ( "github.com/filecoin-project/venus/pkg/crypto" _ "github.com/filecoin-project/venus/pkg/crypto/bls" + _ "github.com/filecoin-project/venus/pkg/crypto/delegated" _ "github.com/filecoin-project/venus/pkg/crypto/secp" tf "github.com/filecoin-project/venus/pkg/testhelpers/testflags" ) @@ -108,6 +109,37 @@ func TestBLSSigning(t *testing.T) { require.Error(t, err) } +func TestDelegatedSigning(t *testing.T) { + token := bytes.Repeat([]byte{42}, 512) + ki, err := crypto.NewDelegatedKeyFromSeed(bytes.NewReader(token)) + assert.NoError(t, err) + + data := []byte("data to be signed") + privateKey := ki.Key() + publicKey, err := ki.PublicKey() + assert.NoError(t, err) + t.Logf("%x", privateKey) + t.Logf("%x", publicKey) + + signature, err := crypto.Sign(data, privateKey[:], crypto.SigTypeDelegated) + require.NoError(t, err) + + addr, err := ki.Address() + require.NoError(t, err) + t.Logf("%v", addr.String()) + + err = crypto.Verify(signature, addr, data) + require.NoError(t, err) + + // invalid signature fails + err = crypto.Verify(&crypto.Signature{Type: crypto.SigTypeDelegated, Data: signature.Data[3:]}, addr, data) + require.Error(t, err) + + // invalid digest fails + err = crypto.Verify(signature, addr, data[3:]) + require.Error(t, err) +} + func aggregateSignatures(sigs []*crypto.Signature) (*crypto.Signature, error) { sigsS := make([]ffi.Signature, len(sigs)) for i := 0; i < len(sigs); i++ { diff --git a/pkg/crypto/delegated/init.go b/pkg/crypto/delegated/init.go index 73f0fa0ac2..509fc02f31 100644 --- a/pkg/crypto/delegated/init.go +++ b/pkg/crypto/delegated/init.go @@ -32,7 +32,15 @@ func (delegatedSigner) ToPublic(pk []byte) ([]byte, error) { } func (delegatedSigner) Sign(pk []byte, msg []byte) ([]byte, error) { - return nil, fmt.Errorf("not implemented") + hasher := sha3.NewLegacyKeccak256() + hasher.Write(msg) + hashSum := hasher.Sum(nil) + sig, err := gocrypto.Sign(pk, hashSum) + if err != nil { + return nil, err + } + + return sig, nil } func (delegatedSigner) Verify(sig []byte, a address.Address, msg []byte) error { diff --git a/pkg/crypto/keyinfo.go b/pkg/crypto/keyinfo.go index 5c06ebd530..df88a93c3f 100644 --- a/pkg/crypto/keyinfo.go +++ b/pkg/crypto/keyinfo.go @@ -7,9 +7,11 @@ import ( "github.com/awnumar/memguard" "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/crypto" logging "github.com/ipfs/go-log/v2" "github.com/pkg/errors" + "golang.org/x/crypto/sha3" ) const ( @@ -150,9 +152,23 @@ func (ki *KeyInfo) Address() (address.Address, error) { if ki.SigType == SigTypeBLS { return address.NewBLSAddress(pubKey) } - if ki.SigType == SigTypeSecp256k1 || ki.SigType == SigTypeDelegated { + if ki.SigType == SigTypeSecp256k1 { return address.NewSecp256k1Address(pubKey) } + if ki.SigType == SigTypeDelegated { + // Assume eth for now + hasher := sha3.NewLegacyKeccak256() + // if we get an uncompressed public key (that's what we get from the library, + // but putting this check here for defensiveness), strip the prefix + if pubKey[0] == 0x04 { + pubKey = pubKey[1:] + } + + hasher.Write(pubKey) + + return address.NewDelegatedAddress(builtin.EthereumAddressManagerActorID, hasher.Sum(nil)[12:]) + } + return address.Undef, errors.Errorf("can not generate address for unknown crypto system: %d", ki.SigType) } diff --git a/pkg/gen/genesis/fevm.go b/pkg/gen/genesis/fevm.go index 6fc51fece5..3df66774a3 100644 --- a/pkg/gen/genesis/fevm.go +++ b/pkg/gen/genesis/fevm.go @@ -64,7 +64,7 @@ func SetupFEVM(ctx context.Context, cs *chain.Store, sroot cid.Cid, nv network.V return fvm.NewVM(ctx, vmopt) } - genesisVm, err := newVM(sroot) + genesisVM, err := newVM(sroot) if err != nil { return cid.Undef, fmt.Errorf("creating vm: %w", err) } @@ -111,11 +111,11 @@ func SetupFEVM(ctx context.Context, cs *chain.Store, sroot cid.Cid, nv network.V // TODO method 3 is Exec4; we have to name the methods in go-state-types and avoid using the number // directly. - if _, err := doExecValue(ctx, genesisVm, builtintypes.InitActorAddr, builtintypes.EthereumAddressManagerActorAddr, big.Zero(), 3, mustEnc(params)); err != nil { + if _, err := doExecValue(ctx, genesisVM, builtintypes.InitActorAddr, builtintypes.EthereumAddressManagerActorAddr, big.Zero(), 3, mustEnc(params)); err != nil { return cid.Undef, fmt.Errorf("creating ETH0 actor: %w", err) } - newroot, err := genesisVm.Flush(ctx) + newroot, err := genesisVM.Flush(ctx) if err != nil { return cid.Undef, fmt.Errorf("flushing vm: %w", err) } diff --git a/pkg/wallet/dsbackend.go b/pkg/wallet/dsbackend.go index bac674a162..2d3dbf9977 100644 --- a/pkg/wallet/dsbackend.go +++ b/pkg/wallet/dsbackend.go @@ -129,13 +129,27 @@ func (backend *DSBackend) NewAddress(ctx context.Context, protocol address.Proto switch protocol { case address.BLS: return backend.newBLSAddress(ctx) - case address.SECP256K1, address.Delegated: + case address.SECP256K1: return backend.newSecpAddress(ctx) + case address.Delegated: + return backend.newDelegatedAddress(ctx) default: return address.Undef, errors.Errorf("Unknown address protocol %d", protocol) } } +func (backend *DSBackend) newDelegatedAddress(ctx context.Context) (address.Address, error) { + ki, err := crypto.NewDelegatedKeyFromSeed(rand.Reader) + if err != nil { + return address.Undef, err + } + + if err := backend.putKeyInfo(ctx, &ki); err != nil { + return address.Undef, err + } + return ki.Address() +} + func (backend *DSBackend) newSecpAddress(ctx context.Context) (address.Address, error) { ki, err := crypto.NewSecpKeyFromSeed(rand.Reader) if err != nil { diff --git a/pkg/wallet/wallet_test.go b/pkg/wallet/wallet_test.go index df5d7bf024..7bb89366d8 100644 --- a/pkg/wallet/wallet_test.go +++ b/pkg/wallet/wallet_test.go @@ -16,6 +16,7 @@ import ( bls "github.com/filecoin-project/filecoin-ffi" "github.com/filecoin-project/venus/pkg/config" "github.com/filecoin-project/venus/pkg/crypto" + _ "github.com/filecoin-project/venus/pkg/crypto/delegated" // enable delegated signatures tf "github.com/filecoin-project/venus/pkg/testhelpers/testflags" ) @@ -132,6 +133,50 @@ func TestWalletBLSKeys(t *testing.T) { }) } +func TestWalletDelegatedKeys(t *testing.T) { + tf.UnitTest(t) + + w, wb := newWalletAndDSBackend(t) + + ctx := context.Background() + addr, err := w.NewAddress(ctx, address.Delegated) + require.NoError(t, err) + + data := []byte("data to be signed") + // stm: @WALLET_WALLET_SIGN_BYTES_001 + sig, err := w.SignBytes(ctx, data, addr) + require.NoError(t, err) + + t.Run("address is delegated protocol", func(t *testing.T) { + assert.Equal(t, address.Delegated, addr.Protocol()) + }) + + t.Run("key uses delegated cryptography", func(t *testing.T) { + ki, err := wb.GetKeyInfo(context.Background(), addr) + require.NoError(t, err) + assert.Equal(t, crypto.SigTypeDelegated, ki.SigType) + }) + + t.Run("valid signatures verify", func(t *testing.T) { + err := crypto.Verify(sig, addr, data) + assert.NoError(t, err) + }) + + t.Run("invalid signatures do not verify", func(t *testing.T) { + notTheData := []byte("not the data") + err := crypto.Verify(sig, addr, notTheData) + assert.Error(t, err) + + notTheSig := &crypto.Signature{ + Type: crypto.SigTypeDelegated, + Data: make([]byte, bls.SignatureBytes), + } + copy(notTheSig.Data[:], "not the sig") + err = crypto.Verify(notTheSig, addr, data) + assert.Error(t, err) + }) +} + func TestSimpleSignAndVerify(t *testing.T) { tf.UnitTest(t) diff --git a/venus-shared/types/eth.go b/venus-shared/types/eth.go index 76f1180ee3..8ab8c4d237 100644 --- a/venus-shared/types/eth.go +++ b/venus-shared/types/eth.go @@ -311,7 +311,7 @@ func EthAddressFromHex(s string) (EthAddress, error) { func EthAddressFromBytes(b []byte) (EthAddress, error) { var a EthAddress if len(b) != EthAddressLength { - return EthAddress{}, xerrors.Errorf("cannot parse bytes into anœ EthAddress: incorrect input length") + return EthAddress{}, xerrors.Errorf("cannot parse bytes into an EthAddress: incorrect input length") } copy(a[:], b[:]) return a, nil diff --git a/venus-shared/utils/method_map.go b/venus-shared/utils/method_map.go index d818cf0b58..90ad74c0af 100644 --- a/venus-shared/utils/method_map.go +++ b/venus-shared/utils/method_map.go @@ -102,7 +102,7 @@ func loadMethodsMap() { methodMeta.Params = et.In(0) } - methods[abi.MethodNum(number)] = methodMeta + methods[number] = methodMeta } MethodsMap[actor.Code()] = methods