From d99d7c91f846117fb472e9013aaba57a9acb8288 Mon Sep 17 00:00:00 2001 From: beer-1 <147697694+beer-1@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:21:03 +0900 Subject: [PATCH] fix signing --- app/ante/ante.go | 2 +- app/ante/sigverify.go | 27 + app/app.go | 1 - app/encoding.go | 1 + cmd/initiad/root.go | 7 +- crypto/codec/amino.go | 1 + crypto/codec/proto.go | 1 + crypto/hd/algo.go | 37 ++ crypto/keys/eth/secp256k1/hash.go | 11 + crypto/keys/eth/secp256k1/keys.pb.go | 503 ++++++++++++++++++ crypto/keys/eth/secp256k1/pubkey.pb.go | 329 ------------ crypto/keys/eth/secp256k1/secp256k1.go | 132 ++++- crypto/keys/eth/secp256k1/secp256k1_cgo.go | 17 +- .../keys/eth/secp256k1/secp256k1_cgo_test.go | 2 +- crypto/keys/eth/secp256k1/secp256k1_nocgo.go | 18 +- .../eth/secp256k1/secp256k1_nocgo_test.go | 6 +- crypto/keys/eth/secp256k1/secp256k1_test.go | 143 ++++- go.mod | 10 +- go.sum | 4 +- .../secp256k1/{pubkey.proto => keys.proto} | 9 + 20 files changed, 900 insertions(+), 361 deletions(-) create mode 100644 app/ante/sigverify.go create mode 100644 crypto/hd/algo.go create mode 100644 crypto/keys/eth/secp256k1/hash.go create mode 100644 crypto/keys/eth/secp256k1/keys.pb.go delete mode 100644 crypto/keys/eth/secp256k1/pubkey.pb.go rename proto/initia/crypto/eth/secp256k1/{pubkey.proto => keys.proto} (82%) diff --git a/app/ante/ante.go b/app/ante/ante.go index 725f4992..48375855 100644 --- a/app/ante/ante.go +++ b/app/ante/ante.go @@ -49,7 +49,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { sigGasConsumer := options.SigGasConsumer if sigGasConsumer == nil { - sigGasConsumer = ante.DefaultSigVerificationGasConsumer + sigGasConsumer = DefaultSigVerificationGasConsumer } txFeeChecker := options.TxFeeChecker diff --git a/app/ante/sigverify.go b/app/ante/sigverify.go new file mode 100644 index 00000000..b040064e --- /dev/null +++ b/app/ante/sigverify.go @@ -0,0 +1,27 @@ +package ante + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + "github.com/initia-labs/initia/crypto/keys/eth/secp256k1" +) + +// DefaultSigVerificationGasConsumer is the default implementation of SignatureVerificationGasConsumer. It consumes gas +// for signature verification based upon the public key type. The cost is fetched from the given params and is matched +// by the concrete type. +func DefaultSigVerificationGasConsumer( + meter sdk.GasMeter, sig signing.SignatureV2, params authtypes.Params, +) error { + pubkey := sig.PubKey + switch pubkey.(type) { + case *secp256k1.PubKey: + meter.ConsumeGas(params.SigVerifyCostSecp256k1, "ante verify: eth_secp256k1") + return nil + + default: + return ante.DefaultSigVerificationGasConsumer(meter, sig, params) + } +} diff --git a/app/app.go b/app/app.go index 02db7e18..9518ccda 100644 --- a/app/app.go +++ b/app/app.go @@ -1091,7 +1091,6 @@ func (app *InitiaApp) setAnteHandler( BankKeeper: app.BankKeeper, FeegrantKeeper: app.FeeGrantKeeper, SignModeHandler: app.txConfig.SignModeHandler(), - SigGasConsumer: cosmosante.DefaultSigVerificationGasConsumer, }, IBCkeeper: app.IBCKeeper, MoveKeeper: movekeeper.NewDexKeeper(app.MoveKeeper), diff --git a/app/encoding.go b/app/encoding.go index 3bf28b87..e043b686 100644 --- a/app/encoding.go +++ b/app/encoding.go @@ -24,6 +24,7 @@ func MakeEncodingConfig() params.EncodingConfig { // authz module use this codec to get signbytes. // authz MsgExec can execute all message types, // so legacy.Cdc need to register all amino messages to get proper signature + cryptocodec.RegisterCrypto(legacy.Cdc) ModuleBasics.RegisterLegacyAminoCodec(legacy.Cdc) legacyCodecRegistered = true } diff --git a/cmd/initiad/root.go b/cmd/initiad/root.go index 97a0d123..34a7f7c0 100644 --- a/cmd/initiad/root.go +++ b/cmd/initiad/root.go @@ -20,6 +20,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/rpc" + "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/server" servertypes "github.com/cosmos/cosmos-sdk/server/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -31,6 +32,7 @@ import ( initiaapp "github.com/initia-labs/initia/app" "github.com/initia-labs/initia/app/params" + initiahd "github.com/initia-labs/initia/crypto/hd" genutilcli "github.com/initia-labs/initia/x/genutil/client/cli" moveconfig "github.com/initia-labs/initia/x/move/config" ) @@ -77,7 +79,10 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) { WithInput(os.Stdin). WithAccountRetriever(types.AccountRetriever{}). WithHomeDir(initiaapp.DefaultNodeHome). - WithViper(initiaapp.EnvPrefix) + WithViper(initiaapp.EnvPrefix). + WithKeyringOptions(func(options *keyring.Options) { + options.SupportedAlgos = append(options.SupportedAlgos, initiahd.EthSecp256k1) + }) rootCmd := &cobra.Command{ Use: basename, diff --git a/crypto/codec/amino.go b/crypto/codec/amino.go index 6154f55e..9985e338 100644 --- a/crypto/codec/amino.go +++ b/crypto/codec/amino.go @@ -10,4 +10,5 @@ import ( // codec. func RegisterCrypto(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(ðsecp256k1.PubKey{}, ethsecp256k1.PubKeyName, nil) + cdc.RegisterConcrete(ðsecp256k1.PrivKey{}, ethsecp256k1.PrivKeyName, nil) } diff --git a/crypto/codec/proto.go b/crypto/codec/proto.go index 0c124799..6b96d5af 100644 --- a/crypto/codec/proto.go +++ b/crypto/codec/proto.go @@ -10,4 +10,5 @@ import ( // RegisterInterfaces registers the sdk.Tx interface. func RegisterInterfaces(registry codectypes.InterfaceRegistry) { registry.RegisterImplementations((*cryptotypes.PubKey)(nil), ðsecp256k1.PubKey{}) + registry.RegisterImplementations((*cryptotypes.PrivKey)(nil), ðsecp256k1.PrivKey{}) } diff --git a/crypto/hd/algo.go b/crypto/hd/algo.go new file mode 100644 index 00000000..a2ee5dc3 --- /dev/null +++ b/crypto/hd/algo.go @@ -0,0 +1,37 @@ +package hd + +import ( + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/types" + + "github.com/initia-labs/initia/crypto/keys/eth/secp256k1" +) + +var ( + // EthSecp256k1Type uses the ethereum secp256k1 ECDSA parameters. + EthSecp256k1Type = hd.PubKeyType("eth_secp256k1") +) + +// EthSecp256k1 uses the Bitcoin secp256k1 ECDSA parameters. +var EthSecp256k1 = ethSecp256k1Algo{} + +type ethSecp256k1Algo struct{} + +func (s ethSecp256k1Algo) Name() hd.PubKeyType { + return EthSecp256k1Type +} + +// Derive derives and returns the secp256k1 private key for the given seed and HD path. +func (s ethSecp256k1Algo) Derive() hd.DeriveFn { + return hd.Secp256k1.Derive() +} + +// Generate generates a eth_secp256k1 private key from the given bytes. +func (s ethSecp256k1Algo) Generate() hd.GenerateFn { + return func(bz []byte) types.PrivKey { + bzArr := make([]byte, secp256k1.PrivKeySize) + copy(bzArr, bz) + + return &secp256k1.PrivKey{Key: bzArr} + } +} diff --git a/crypto/keys/eth/secp256k1/hash.go b/crypto/keys/eth/secp256k1/hash.go new file mode 100644 index 00000000..3850a441 --- /dev/null +++ b/crypto/keys/eth/secp256k1/hash.go @@ -0,0 +1,11 @@ +package secp256k1 + +import "golang.org/x/crypto/sha3" + +func Keccak256(msg []byte) []byte { + hasher := sha3.NewLegacyKeccak256() + if _, err := hasher.Write(msg); err != nil { + panic(err) + } + return hasher.Sum(nil) +} diff --git a/crypto/keys/eth/secp256k1/keys.pb.go b/crypto/keys/eth/secp256k1/keys.pb.go new file mode 100644 index 00000000..197b646f --- /dev/null +++ b/crypto/keys/eth/secp256k1/keys.pb.go @@ -0,0 +1,503 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: initia/crypto/eth/secp256k1/keys.proto + +package secp256k1 + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// PubKey defines a eth_secp256k1 public key +// Key is the compressed form of the pubkey. The first byte depends is a 0x02 byte +// if the y-coordinate is the lexicographically largest of the two associated with +// the x-coordinate. Otherwise the first byte is a 0x03. +// This prefix is followed with the x-coordinate. +type PubKey struct { + Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` +} + +func (m *PubKey) Reset() { *m = PubKey{} } +func (*PubKey) ProtoMessage() {} +func (*PubKey) Descriptor() ([]byte, []int) { + return fileDescriptor_287ef21c8591c00d, []int{0} +} +func (m *PubKey) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PubKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PubKey.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PubKey) XXX_Merge(src proto.Message) { + xxx_messageInfo_PubKey.Merge(m, src) +} +func (m *PubKey) XXX_Size() int { + return m.Size() +} +func (m *PubKey) XXX_DiscardUnknown() { + xxx_messageInfo_PubKey.DiscardUnknown(m) +} + +var xxx_messageInfo_PubKey proto.InternalMessageInfo + +func (m *PubKey) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +// PrivKey defines a eth_secp256k1 private key. +type PrivKey struct { + Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` +} + +func (m *PrivKey) Reset() { *m = PrivKey{} } +func (m *PrivKey) String() string { return proto.CompactTextString(m) } +func (*PrivKey) ProtoMessage() {} +func (*PrivKey) Descriptor() ([]byte, []int) { + return fileDescriptor_287ef21c8591c00d, []int{1} +} +func (m *PrivKey) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PrivKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PrivKey.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PrivKey) XXX_Merge(src proto.Message) { + xxx_messageInfo_PrivKey.Merge(m, src) +} +func (m *PrivKey) XXX_Size() int { + return m.Size() +} +func (m *PrivKey) XXX_DiscardUnknown() { + xxx_messageInfo_PrivKey.DiscardUnknown(m) +} + +var xxx_messageInfo_PrivKey proto.InternalMessageInfo + +func (m *PrivKey) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func init() { + proto.RegisterType((*PubKey)(nil), "initia.crypto.eth.secp256k1.PubKey") + proto.RegisterType((*PrivKey)(nil), "initia.crypto.eth.secp256k1.PrivKey") +} + +func init() { + proto.RegisterFile("initia/crypto/eth/secp256k1/keys.proto", fileDescriptor_287ef21c8591c00d) +} + +var fileDescriptor_287ef21c8591c00d = []byte{ + // 246 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xcb, 0xcc, 0xcb, 0x2c, + 0xc9, 0x4c, 0xd4, 0x4f, 0x2e, 0xaa, 0x2c, 0x28, 0xc9, 0xd7, 0x4f, 0x2d, 0xc9, 0xd0, 0x2f, 0x4e, + 0x4d, 0x2e, 0x30, 0x32, 0x35, 0xcb, 0x36, 0xd4, 0xcf, 0x4e, 0xad, 0x2c, 0xd6, 0x2b, 0x28, 0xca, + 0x2f, 0xc9, 0x17, 0x92, 0x86, 0xa8, 0xd3, 0x83, 0xa8, 0xd3, 0x4b, 0x2d, 0xc9, 0xd0, 0x83, 0xab, + 0x93, 0x12, 0x4c, 0xcc, 0xcd, 0xcc, 0xcb, 0xd7, 0x07, 0x93, 0x10, 0xf5, 0x52, 0x22, 0xe9, 0xf9, + 0xe9, 0xf9, 0x60, 0xa6, 0x3e, 0x88, 0x05, 0x11, 0x55, 0xf2, 0xe1, 0x62, 0x0b, 0x28, 0x4d, 0xf2, + 0x4e, 0xad, 0x14, 0x12, 0xe0, 0x62, 0xce, 0x4e, 0xad, 0x94, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x09, + 0x02, 0x31, 0xad, 0x0c, 0x66, 0x2c, 0x90, 0x67, 0xe8, 0x7a, 0xbe, 0x41, 0x4b, 0x12, 0xea, 0x24, + 0x88, 0x4a, 0xd7, 0x92, 0x8c, 0x60, 0x98, 0x45, 0x93, 0x9e, 0x6f, 0xd0, 0xe2, 0xcc, 0x4e, 0xad, + 0x8c, 0x4f, 0xcb, 0x4c, 0xcd, 0x49, 0x51, 0xf2, 0xe2, 0x62, 0x0f, 0x28, 0xca, 0x2c, 0xc3, 0x6e, + 0x9c, 0x2e, 0xc8, 0x28, 0x29, 0x98, 0x51, 0x10, 0x65, 0xb8, 0xcd, 0x72, 0x0a, 0x3c, 0xf1, 0x48, + 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, + 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, 0x86, 0x28, 0xf3, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, + 0xe4, 0xfc, 0x5c, 0x7d, 0x88, 0x71, 0xba, 0x39, 0x89, 0x49, 0xc5, 0xfa, 0xa8, 0x01, 0x07, 0x0a, + 0x2a, 0xd4, 0xd0, 0x4b, 0x62, 0x03, 0xfb, 0xd9, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xa6, 0x98, + 0x3a, 0xa6, 0x63, 0x01, 0x00, 0x00, +} + +func (m *PubKey) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PubKey) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PubKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintKeys(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PrivKey) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PrivKey) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PrivKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintKeys(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintKeys(dAtA []byte, offset int, v uint64) int { + offset -= sovKeys(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *PubKey) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovKeys(uint64(l)) + } + return n +} + +func (m *PrivKey) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovKeys(uint64(l)) + } + return n +} + +func sovKeys(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozKeys(x uint64) (n int) { + return sovKeys(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *PubKey) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKeys + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PubKey: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PubKey: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKeys + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthKeys + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthKeys + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) + if m.Key == nil { + m.Key = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipKeys(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthKeys + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PrivKey) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKeys + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PrivKey: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PrivKey: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKeys + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthKeys + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthKeys + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) + if m.Key == nil { + m.Key = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipKeys(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthKeys + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipKeys(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowKeys + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowKeys + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowKeys + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthKeys + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupKeys + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthKeys + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthKeys = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowKeys = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupKeys = fmt.Errorf("proto: unexpected end of group") +) diff --git a/crypto/keys/eth/secp256k1/pubkey.pb.go b/crypto/keys/eth/secp256k1/pubkey.pb.go deleted file mode 100644 index 4158b564..00000000 --- a/crypto/keys/eth/secp256k1/pubkey.pb.go +++ /dev/null @@ -1,329 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: initia/crypto/eth/secp256k1/pubkey.proto - -package secp256k1 - -import ( - fmt "fmt" - _ "github.com/cosmos/cosmos-sdk/types/tx/amino" - _ "github.com/cosmos/gogoproto/gogoproto" - proto "github.com/cosmos/gogoproto/proto" - io "io" - math "math" - math_bits "math/bits" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package - -// PubKey defines a eth_secp256k1 public key -// Key is the compressed form of the pubkey. The first byte depends is a 0x02 byte -// if the y-coordinate is the lexicographically largest of the two associated with -// the x-coordinate. Otherwise the first byte is a 0x03. -// This prefix is followed with the x-coordinate. -type PubKey struct { - Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` -} - -func (m *PubKey) Reset() { *m = PubKey{} } -func (*PubKey) ProtoMessage() {} -func (*PubKey) Descriptor() ([]byte, []int) { - return fileDescriptor_8fdf6a8c6891b59f, []int{0} -} -func (m *PubKey) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PubKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PubKey.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *PubKey) XXX_Merge(src proto.Message) { - xxx_messageInfo_PubKey.Merge(m, src) -} -func (m *PubKey) XXX_Size() int { - return m.Size() -} -func (m *PubKey) XXX_DiscardUnknown() { - xxx_messageInfo_PubKey.DiscardUnknown(m) -} - -var xxx_messageInfo_PubKey proto.InternalMessageInfo - -func (m *PubKey) GetKey() []byte { - if m != nil { - return m.Key - } - return nil -} - -func init() { - proto.RegisterType((*PubKey)(nil), "initia.crypto.eth.secp256k1.PubKey") -} - -func init() { - proto.RegisterFile("initia/crypto/eth/secp256k1/pubkey.proto", fileDescriptor_8fdf6a8c6891b59f) -} - -var fileDescriptor_8fdf6a8c6891b59f = []byte{ - // 229 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0xc8, 0xcc, 0xcb, 0x2c, - 0xc9, 0x4c, 0xd4, 0x4f, 0x2e, 0xaa, 0x2c, 0x28, 0xc9, 0xd7, 0x4f, 0x2d, 0xc9, 0xd0, 0x2f, 0x4e, - 0x4d, 0x2e, 0x30, 0x32, 0x35, 0xcb, 0x36, 0xd4, 0x2f, 0x28, 0x4d, 0xca, 0x4e, 0xad, 0xd4, 0x2b, - 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x86, 0xa8, 0xd4, 0x83, 0xa8, 0xd4, 0x4b, 0x2d, 0xc9, 0xd0, - 0x83, 0xab, 0x94, 0x12, 0x4c, 0xcc, 0xcd, 0xcc, 0xcb, 0xd7, 0x07, 0x93, 0x10, 0xf5, 0x52, 0x22, - 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0xa6, 0x3e, 0x88, 0x05, 0x11, 0x55, 0xf2, 0xe1, 0x62, 0x0b, 0x28, - 0x4d, 0xf2, 0x4e, 0xad, 0x14, 0x12, 0xe0, 0x62, 0xce, 0x4e, 0xad, 0x94, 0x60, 0x54, 0x60, 0xd4, - 0xe0, 0x09, 0x02, 0x31, 0xad, 0x0c, 0x66, 0x2c, 0x90, 0x67, 0xe8, 0x7a, 0xbe, 0x41, 0x4b, 0x12, - 0xea, 0x28, 0x88, 0x4a, 0xd7, 0x92, 0x8c, 0x60, 0x98, 0x45, 0x93, 0x9e, 0x6f, 0xd0, 0xe2, 0xcc, - 0x4e, 0xad, 0x8c, 0x4f, 0xcb, 0x4c, 0xcd, 0x49, 0x71, 0x0a, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, - 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, - 0xc6, 0x63, 0x39, 0x86, 0x28, 0xf3, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, - 0x7d, 0x88, 0x69, 0xba, 0x39, 0x89, 0x49, 0xc5, 0xfa, 0xa8, 0xde, 0xcd, 0x4e, 0xad, 0x2c, 0x46, - 0xf5, 0x73, 0x12, 0x1b, 0xd8, 0x9d, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd8, 0x8c, 0xe5, - 0x5d, 0x19, 0x01, 0x00, 0x00, -} - -func (m *PubKey) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *PubKey) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PubKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Key) > 0 { - i -= len(m.Key) - copy(dAtA[i:], m.Key) - i = encodeVarintPubkey(dAtA, i, uint64(len(m.Key))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func encodeVarintPubkey(dAtA []byte, offset int, v uint64) int { - offset -= sovPubkey(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *PubKey) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Key) - if l > 0 { - n += 1 + l + sovPubkey(uint64(l)) - } - return n -} - -func sovPubkey(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozPubkey(x uint64) (n int) { - return sovPubkey(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *PubKey) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPubkey - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: PubKey: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: PubKey: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPubkey - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthPubkey - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthPubkey - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) - if m.Key == nil { - m.Key = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipPubkey(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthPubkey - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipPubkey(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowPubkey - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowPubkey - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowPubkey - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthPubkey - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupPubkey - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthPubkey - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLengthPubkey = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowPubkey = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupPubkey = fmt.Errorf("proto: unexpected end of group") -) diff --git a/crypto/keys/eth/secp256k1/secp256k1.go b/crypto/keys/eth/secp256k1/secp256k1.go index b626b8d5..973d89a2 100644 --- a/crypto/keys/eth/secp256k1/secp256k1.go +++ b/crypto/keys/eth/secp256k1/secp256k1.go @@ -2,7 +2,11 @@ package secp256k1 import ( "bytes" + "crypto/sha256" + "crypto/subtle" "fmt" + io "io" + "math/big" secp256k1 "github.com/btcsuite/btcd/btcec/v2" "github.com/cometbft/cometbft/crypto" //nolint: staticcheck @@ -14,10 +18,121 @@ import ( ) const ( - keyType = "eth_secp256k1" - PubKeyName = "initia/PubKeyEthSecp256k1" + PrivKeySize = 32 + keyType = "eth_secp256k1" + PrivKeyName = "initia/PrivKeyEthSecp256k1" + PubKeyName = "initia/PubKeyEthSecp256k1" ) +// Bytes returns the byte representation of the Private Key. +func (privKey *PrivKey) Bytes() []byte { + return privKey.Key +} + +// PubKey performs the point-scalar multiplication from the privKey on the +// generator point to get the pubkey. +func (privKey *PrivKey) PubKey() cryptotypes.PubKey { + _, pubkeyObject := secp256k1.PrivKeyFromBytes(privKey.Key) + pk := pubkeyObject.SerializeCompressed() + return &PubKey{Key: pk} +} + +// Equals - you probably don't need to use this. +// Runs in constant time based on length of the +func (privKey *PrivKey) Equals(other cryptotypes.LedgerPrivKey) bool { + return privKey.Type() == other.Type() && subtle.ConstantTimeCompare(privKey.Bytes(), other.Bytes()) == 1 +} + +func (privKey *PrivKey) Type() string { + return keyType +} + +// MarshalAmino overrides Amino binary marshalling. +func (privKey PrivKey) MarshalAmino() ([]byte, error) { + return privKey.Key, nil +} + +// UnmarshalAmino overrides Amino binary marshalling. +func (privKey *PrivKey) UnmarshalAmino(bz []byte) error { + if len(bz) != PrivKeySize { + return fmt.Errorf("invalid privkey size") + } + privKey.Key = bz + + return nil +} + +// MarshalAminoJSON overrides Amino JSON marshalling. +func (privKey PrivKey) MarshalAminoJSON() ([]byte, error) { + // When we marshal to Amino JSON, we don't marshal the "key" field itself, + // just its contents (i.e. the key bytes). + return privKey.MarshalAmino() +} + +// UnmarshalAminoJSON overrides Amino JSON marshalling. +func (privKey *PrivKey) UnmarshalAminoJSON(bz []byte) error { + return privKey.UnmarshalAmino(bz) +} + +// GenPrivKey generates a new ECDSA private key on curve secp256k1 private key. +// It uses OS randomness to generate the private key. +func GenPrivKey() *PrivKey { + return &PrivKey{Key: genPrivKey(crypto.CReader())} +} + +// genPrivKey generates a new secp256k1 private key using the provided reader. +func genPrivKey(rand io.Reader) []byte { + var privKeyBytes [PrivKeySize]byte + d := new(big.Int) + for { + privKeyBytes = [PrivKeySize]byte{} + _, err := io.ReadFull(rand, privKeyBytes[:]) + if err != nil { + panic(err) + } + + d.SetBytes(privKeyBytes[:]) + // break if we found a valid point (i.e. > 0 and < N == curverOrder) + isValidFieldElement := 0 < d.Sign() && d.Cmp(secp256k1.S256().N) < 0 + if isValidFieldElement { + break + } + } + + return privKeyBytes[:] +} + +var one = new(big.Int).SetInt64(1) + +// GenPrivKeyFromSecret hashes the secret with SHA2, and uses +// that 32 byte output to create the private key. +// +// It makes sure the private key is a valid field element by setting: +// +// c = sha256(secret) +// k = (c mod (n − 1)) + 1, where n = curve order. +// +// NOTE: secret should be the output of a KDF like bcrypt, +// if it's derived from user input. +func GenPrivKeyFromSecret(secret []byte) *PrivKey { + secHash := sha256.Sum256(secret) + // to guarantee that we have a valid field element, we use the approach of: + // "Suite B Implementer’s Guide to FIPS 186-3", A.2.1 + // https://apps.nsa.gov/iaarchive/library/ia-guidance/ia-solutions-for-classified/algorithm-guidance/suite-b-implementers-guide-to-fips-186-3-ecdsa.cfm + // see also https://github.com/golang/go/blob/0380c9ad38843d523d9c9804fe300cb7edd7cd3c/src/crypto/ecdsa/ecdsa.go#L89-L101 + fe := new(big.Int).SetBytes(secHash[:]) + n := new(big.Int).Sub(secp256k1.S256().N, one) + fe.Mod(fe, n) + fe.Add(fe, one) + + feB := fe.Bytes() + privKey32 := make([]byte, PrivKeySize) + // copy feB over to fixed 32 byte privKey32 and pad (if necessary) + copy(privKey32[32-len(feB):32], feB) + + return &PrivKey{Key: privKey32} +} + //------------------------------------- var ( @@ -91,3 +206,16 @@ func (pubKey PubKey) MarshalAminoJSON() ([]byte, error) { func (pubKey *PubKey) UnmarshalAminoJSON(bz []byte) error { return pubKey.UnmarshalAmino(bz) } + +// // VerifySignature validates the signature. +// // The msg will be hashed prior to signature verification. +// func (pubKey *PubKey) VerifySignature(msg []byte, sigStr []byte) bool { +// hasher := sha3.NewLegacyKeccak256() +// if _, err := hasher.Write(msg); err != nil { +// panic(err) +// } +// msg = hasher.Sum(nil) + +// pk := cosmossecp256k1.PubKey{Key: pubKey.Key} +// return pk.VerifySignature(msg, sigStr) +// } diff --git a/crypto/keys/eth/secp256k1/secp256k1_cgo.go b/crypto/keys/eth/secp256k1/secp256k1_cgo.go index b58a4981..a5b73e7e 100644 --- a/crypto/keys/eth/secp256k1/secp256k1_cgo.go +++ b/crypto/keys/eth/secp256k1/secp256k1_cgo.go @@ -4,13 +4,22 @@ package secp256k1 import ( - "github.com/cometbft/cometbft/crypto" - - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1/internal/secp256k1" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1/cgo/secp256k1" ) +// Sign creates an ECDSA signature on curve Secp256k1, using SHA256 on the msg. +func (privKey *PrivKey) Sign(msg []byte) ([]byte, error) { + rsv, err := secp256k1.Sign(Keccak256(msg), privKey.Key) + if err != nil { + return nil, err + } + // we do not need v in r||s||v: + rs := rsv[:len(rsv)-1] + return rs, nil +} + // VerifySignature validates the signature. // The msg will be hashed prior to signature verification. func (pubKey *PubKey) VerifySignature(msg []byte, sigStr []byte) bool { - return secp256k1.VerifySignature(pubKey.Bytes(), crypto.Sha256(msg), sigStr) + return secp256k1.VerifySignature(pubKey.Bytes(), Keccak256(msg), sigStr) } diff --git a/crypto/keys/eth/secp256k1/secp256k1_cgo_test.go b/crypto/keys/eth/secp256k1/secp256k1_cgo_test.go index 00128e92..2023175e 100644 --- a/crypto/keys/eth/secp256k1/secp256k1_cgo_test.go +++ b/crypto/keys/eth/secp256k1/secp256k1_cgo_test.go @@ -34,7 +34,7 @@ func TestPrivKeySecp256k1SignVerify(t *testing.T) { require.NoError(t, err) require.NotNil(t, got) - pub := &PubKey{Key: tt.privKey.PubKey().Bytes()} + pub := tt.privKey.PubKey() assert.Equal(t, tt.wantVerifyPasses, pub.VerifySignature(msg, got)) }) } diff --git a/crypto/keys/eth/secp256k1/secp256k1_nocgo.go b/crypto/keys/eth/secp256k1/secp256k1_nocgo.go index ac6ad2e1..982f2de3 100644 --- a/crypto/keys/eth/secp256k1/secp256k1_nocgo.go +++ b/crypto/keys/eth/secp256k1/secp256k1_nocgo.go @@ -6,10 +6,21 @@ package secp256k1 import ( secp256k1 "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/ecdsa" - - "github.com/cometbft/cometbft/crypto" ) +// Sign creates an ECDSA signature on curve Secp256k1, using SHA256 on the msg. +// The returned signature will be of the form R || S (in lower-S form). +func (privKey *PrivKey) Sign(msg []byte) ([]byte, error) { + priv, _ := secp256k1.PrivKeyFromBytes(privKey.Key) + sig, err := ecdsa.SignCompact(priv, Keccak256(msg), false) + if err != nil { + return nil, err + } + + // remove the first byte which is compactSigRecoveryCode + return sig[1:], nil +} + // VerifyBytes verifies a signature of the form R || S. // It rejects signatures which are not in lower-S form. func (pubKey *PubKey) VerifySignature(msg []byte, sigStr []byte) bool { @@ -33,7 +44,8 @@ func (pubKey *PubKey) VerifySignature(msg []byte, sigStr []byte) bool { if !signature.IsEqual(modifiedSignature) { return false } - return signature.Verify(crypto.Sha256(msg), pub) + + return signature.Verify(Keccak256(msg), pub) } // Read Signature struct from R || S. Caller needs to ensure diff --git a/crypto/keys/eth/secp256k1/secp256k1_nocgo_test.go b/crypto/keys/eth/secp256k1/secp256k1_nocgo_test.go index 1941349c..f38a5bf4 100644 --- a/crypto/keys/eth/secp256k1/secp256k1_nocgo_test.go +++ b/crypto/keys/eth/secp256k1/secp256k1_nocgo_test.go @@ -8,8 +8,6 @@ import ( secp256k1 "github.com/btcsuite/btcd/btcec/v2" "github.com/stretchr/testify/require" - - cosmoskey "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" ) // Ensure that signature verification works, and that @@ -18,7 +16,7 @@ import ( func TestSignatureVerificationAndRejectUpperS(t *testing.T) { msg := []byte("We have lingered long enough on the shores of the cosmic ocean.") for i := 0; i < 500; i++ { - priv := cosmoskey.GenPrivKey() + priv := GenPrivKey() sigStr, err := priv.Sign(msg) require.NoError(t, err) var r secp256k1.ModNScalar @@ -27,7 +25,7 @@ func TestSignatureVerificationAndRejectUpperS(t *testing.T) { s.SetByteSlice(sigStr[32:64]) require.False(t, s.IsOverHalfOrder()) - pub := &PubKey{Key: priv.PubKey().Bytes()} + pub := priv.PubKey() require.True(t, pub.VerifySignature(msg, sigStr)) // malleate: diff --git a/crypto/keys/eth/secp256k1/secp256k1_test.go b/crypto/keys/eth/secp256k1/secp256k1_test.go index ed6f92cb..918f29c5 100644 --- a/crypto/keys/eth/secp256k1/secp256k1_test.go +++ b/crypto/keys/eth/secp256k1/secp256k1_test.go @@ -10,15 +10,15 @@ import ( btcSecp256k1 "github.com/btcsuite/btcd/btcec/v2" btcecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/cometbft/cometbft/crypto" + tmsecp256k1 "github.com/cometbft/cometbft/crypto/secp256k1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - ethsecp256k1 "github.com/initia-labs/initia/crypto/keys/eth/secp256k1" + "github.com/initia-labs/initia/crypto/keys/eth/secp256k1" ) type keyData struct { @@ -44,17 +44,18 @@ func TestPubKeySecp256k1Address(t *testing.T) { priv := secp256k1.PrivKey{Key: privB} - pubKey := ðsecp256k1.PubKey{Key: priv.PubKey().Bytes()} + pubKey := priv.PubKey() + pubT, _ := pubKey.(*secp256k1.PubKey) addr := pubKey.Address() - assert.Equal(t, pubKey, ðsecp256k1.PubKey{Key: pubB}, "Expected pub keys to match") + assert.Equal(t, pubT, &secp256k1.PubKey{Key: pubB}, "Expected pub keys to match") assert.Equal(t, addr, addrB, "Expected addresses to match") } } func TestSignAndValidateSecp256k1(t *testing.T) { privKey := secp256k1.GenPrivKey() - pubKey := ðsecp256k1.PubKey{Key: privKey.PubKey().Bytes()} + pubKey := privKey.PubKey() msg := crypto.CRandBytes(1000) sig, err := privKey.Sign(msg) @@ -110,8 +111,38 @@ func TestSecp256k1LoadPrivkeyAndSerializeIsIdentity(t *testing.T) { } } +func TestGenPrivKeyFromSecret(t *testing.T) { + // curve oder N + N := btcSecp256k1.S256().N + tests := []struct { + name string + secret []byte + }{ + {"empty secret", []byte{}}, + { + "some long secret", + []byte("We live in a society exquisitely dependent on science and technology, " + + "in which hardly anyone knows anything about science and technology."), + }, + {"another seed used in cosmos tests #1", []byte{0}}, + {"another seed used in cosmos tests #2", []byte("mySecret")}, + {"another seed used in cosmos tests #3", []byte("")}, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + gotPrivKey := secp256k1.GenPrivKeyFromSecret(tt.secret) + require.NotNil(t, gotPrivKey) + // interpret as a big.Int and make sure it is a valid field element: + fe := new(big.Int).SetBytes(gotPrivKey.Key[:]) + require.True(t, fe.Cmp(N) < 0) + require.True(t, fe.Sign() > 0) + }) + } +} + func TestPubKeyEquals(t *testing.T) { - secp256K1PubKey := ðsecp256k1.PubKey{Key: secp256k1.GenPrivKey().PubKey().Bytes()} + secp256K1PubKey := secp256k1.GenPrivKey().PubKey().(*secp256k1.PubKey) testCases := []struct { msg string @@ -122,13 +153,13 @@ func TestPubKeyEquals(t *testing.T) { { "different bytes", secp256K1PubKey, - ðsecp256k1.PubKey{Key: secp256k1.GenPrivKey().PubKey().Bytes()}, + secp256k1.GenPrivKey().PubKey(), false, }, { "equals", secp256K1PubKey, - ðsecp256k1.PubKey{ + &secp256k1.PubKey{ Key: secp256K1PubKey.Key, }, true, @@ -149,10 +180,49 @@ func TestPubKeyEquals(t *testing.T) { } } +func TestPrivKeyEquals(t *testing.T) { + secp256K1PrivKey := secp256k1.GenPrivKey() + + testCases := []struct { + msg string + privKey cryptotypes.PrivKey + other cryptotypes.PrivKey + expectEq bool + }{ + { + "different bytes", + secp256K1PrivKey, + secp256k1.GenPrivKey(), + false, + }, + { + "equals", + secp256K1PrivKey, + &secp256k1.PrivKey{ + Key: secp256K1PrivKey.Key, + }, + true, + }, + { + "different types", + secp256K1PrivKey, + ed25519.GenPrivKey(), + false, + }, + } + + for _, tc := range testCases { + t.Run(tc.msg, func(t *testing.T) { + eq := tc.privKey.Equals(tc.other) + require.Equal(t, eq, tc.expectEq) + }) + } +} + func TestMarshalAmino(t *testing.T) { aminoCdc := codec.NewLegacyAmino() privKey := secp256k1.GenPrivKey() - pubKey := ðsecp256k1.PubKey{Key: privKey.PubKey().Bytes()} + pubKey := privKey.PubKey().(*secp256k1.PubKey) testCases := []struct { desc string @@ -171,7 +241,7 @@ func TestMarshalAmino(t *testing.T) { { "secp256k1 public key", pubKey, - ðsecp256k1.PubKey{}, + &secp256k1.PubKey{}, append([]byte{33}, pubKey.Bytes()...), // Length-prefixed. "\"" + base64.StdEncoding.EncodeToString(pubKey.Bytes()) + "\"", }, @@ -201,3 +271,56 @@ func TestMarshalAmino(t *testing.T) { }) } } + +func TestMarshalAmino_BackwardsCompatibility(t *testing.T) { + aminoCdc := codec.NewLegacyAmino() + // Create Tendermint keys. + tmPrivKey := tmsecp256k1.GenPrivKey() + tmPubKey := tmPrivKey.PubKey() + // Create our own keys, with the same private key as Tendermint's. + privKey := &secp256k1.PrivKey{Key: []byte(tmPrivKey)} + pubKey := privKey.PubKey().(*secp256k1.PubKey) + + testCases := []struct { + desc string + tmKey interface{} + ourKey interface{} + marshalFn func(o interface{}) ([]byte, error) + }{ + { + "secp256k1 private key, binary", + tmPrivKey, + privKey, + aminoCdc.Marshal, + }, + { + "secp256k1 private key, JSON", + tmPrivKey, + privKey, + aminoCdc.MarshalJSON, + }, + { + "secp256k1 public key, binary", + tmPubKey, + pubKey, + aminoCdc.Marshal, + }, + { + "secp256k1 public key, JSON", + tmPubKey, + pubKey, + aminoCdc.MarshalJSON, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + // Make sure Amino encoding override is not breaking backwards compatibility. + bz1, err := tc.marshalFn(tc.tmKey) + require.NoError(t, err) + bz2, err := tc.marshalFn(tc.ourKey) + require.NoError(t, err) + require.Equal(t, bz1, bz2) + }) + } +} diff --git a/go.mod b/go.mod index ab09a5ec..49c8fe0f 100644 --- a/go.mod +++ b/go.mod @@ -44,6 +44,11 @@ require ( gopkg.in/yaml.v3 v3.0.1 ) +require ( + github.com/btcsuite/btcd/btcec/v2 v2.3.2 + github.com/magiconair/properties v1.8.7 +) + require ( cloud.google.com/go v0.110.8 // indirect cloud.google.com/go/compute v1.23.0 // indirect @@ -61,7 +66,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -132,7 +136,6 @@ require ( github.com/lib/pq v1.10.9 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/linxGnu/grocksdb v1.7.16 // indirect - github.com/magiconair/properties v1.8.7 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect @@ -188,7 +191,8 @@ replace ( // use cosmos fork of keyring github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0 - github.com/cosmos/cosmos-sdk => github.com/initia-labs/cosmos-sdk v0.47.7-0.20231123105913-ebb56e6fbd16 + github.com/cosmos/cosmos-sdk => github.com/initia-labs/cosmos-sdk v0.47.7-0.20231201141907-b7635080faba + // github.com/cosmos/cosmos-sdk => ../cosmos-sdk // dgrijalva/jwt-go is deprecated and doesn't receive security updates. // TODO: remove it: https://github.com/cosmos/cosmos-sdk/issues/13134 diff --git a/go.sum b/go.sum index 078ae516..80b4099a 100644 --- a/go.sum +++ b/go.sum @@ -782,8 +782,8 @@ github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mq github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= github.com/initia-labs/OPinit v0.1.0-beta.2 h1:JTtgkPWg/11FrWqnNx6Wl/DBMafco++6oXLHJLh6D/g= github.com/initia-labs/OPinit v0.1.0-beta.2/go.mod h1:MPnFU2x4xzeT3L8K3lc0wkOl09o/oekEabPHIwWdqJg= -github.com/initia-labs/cosmos-sdk v0.47.7-0.20231123105913-ebb56e6fbd16 h1:QoF6V/9XvEuaThvP/jM0MvHlwVe3kn9/hvTPMXH/1ro= -github.com/initia-labs/cosmos-sdk v0.47.7-0.20231123105913-ebb56e6fbd16/go.mod h1:xTc1chW8HyUWCfrgGbjS5jNu9RzlPVrBNfbL9RmZUio= +github.com/initia-labs/cosmos-sdk v0.47.7-0.20231201141907-b7635080faba h1:rG2T0zqE782/IQ3LvSEpDg751WifJ6Hs/ByzrETk6RE= +github.com/initia-labs/cosmos-sdk v0.47.7-0.20231201141907-b7635080faba/go.mod h1:xTc1chW8HyUWCfrgGbjS5jNu9RzlPVrBNfbL9RmZUio= github.com/initia-labs/initiavm v0.1.2-beta.3 h1:+KTrSZIV3355I+KeRhX4LEex3M7enro0oWSjLZtzZGA= github.com/initia-labs/initiavm v0.1.2-beta.3/go.mod h1:aQt4lImZWF9xj7Fm978n0IoeDdKWXTg1CSq6O4WoSD8= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= diff --git a/proto/initia/crypto/eth/secp256k1/pubkey.proto b/proto/initia/crypto/eth/secp256k1/keys.proto similarity index 82% rename from proto/initia/crypto/eth/secp256k1/pubkey.proto rename to proto/initia/crypto/eth/secp256k1/keys.proto index 89a110a7..9637c868 100644 --- a/proto/initia/crypto/eth/secp256k1/pubkey.proto +++ b/proto/initia/crypto/eth/secp256k1/keys.proto @@ -28,3 +28,12 @@ message PubKey { bytes key = 1; } + +// PrivKey defines a eth_secp256k1 private key. +message PrivKey { + option (amino.name) = "initia/PrivKeyEthSecp256k1"; + option (amino.message_encoding) = "key_field"; + + bytes key = 1; +} +