diff --git a/CHANGELOG.md b/CHANGELOG.md index 25c2a1a9e66..46dd4eb05a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Bug Fixes +* (crypto) [#20027](https://github.com/cosmos/cosmos-sdk/pull/20027) secp256r1 keys now implement gogoproto's customtype interface. * (x/gov) [#19725](https://github.com/cosmos/cosmos-sdk/pull/19725) Fetch a failed proposal tally from proposal.FinalTallyResult in the gprc query. * (crypto) [#19691](https://github.com/cosmos/cosmos-sdk/pull/19746) Fix tx sign doesn't throw an error when incorrect Ledger is used. diff --git a/crypto/keys/secp256r1/privkey.go b/crypto/keys/secp256r1/privkey.go index c6702111c73..a0ae1b069bc 100644 --- a/crypto/keys/secp256r1/privkey.go +++ b/crypto/keys/secp256r1/privkey.go @@ -1,10 +1,14 @@ package secp256r1 import ( + "encoding/base64" + "github.com/cosmos/cosmos-sdk/crypto/keys/internal/ecdsa" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" ) +var _ customProtobufType = (*ecdsaSK)(nil) + // GenPrivKey generates a new secp256r1 private key. It uses operating system randomness. func GenPrivKey() (*PrivKey, error) { key, err := ecdsa.GenPrivKey(secp256r1) @@ -52,6 +56,27 @@ type ecdsaSK struct { ecdsa.PrivKey } +// Marshal implements customProtobufType. +func (sk ecdsaSK) Marshal() ([]byte, error) { + return sk.PrivKey.Bytes(), nil +} + +// MarshalJSON implements customProtobufType. +func (sk ecdsaSK) MarshalJSON() ([]byte, error) { + b64 := base64.StdEncoding.EncodeToString(sk.PrivKey.Bytes()) + return []byte(b64), nil +} + +// UnmarshalJSON implements customProtobufType. +func (sk *ecdsaSK) UnmarshalJSON(data []byte) error { + bz, err := base64.StdEncoding.DecodeString(string(data)) + if err != nil { + return err + } + + return sk.PrivKey.Unmarshal(bz, secp256r1, fieldSize) +} + // Size implements proto.Marshaler interface func (sk *ecdsaSK) Size() int { if sk == nil { diff --git a/crypto/keys/secp256r1/privkey_internal_test.go b/crypto/keys/secp256r1/privkey_internal_test.go index 771582d6a41..11b234191fc 100644 --- a/crypto/keys/secp256r1/privkey_internal_test.go +++ b/crypto/keys/secp256r1/privkey_internal_test.go @@ -113,3 +113,14 @@ func (suite *SKSuite) TestSize() { var nilPk *ecdsaSK require.Equal(0, nilPk.Size(), "nil value must have zero size") } + +func (suite *SKSuite) TestJson() { + require := suite.Require() + asd := suite.sk.(*PrivKey) + bz, err := asd.Secret.MarshalJSON() + require.NoError(err) + + sk := &ecdsaSK{} + require.NoError(sk.UnmarshalJSON(bz)) + require.Equal(suite.sk.(*PrivKey).Secret, sk) +} diff --git a/crypto/keys/secp256r1/pubkey.go b/crypto/keys/secp256r1/pubkey.go index bf79c33cb9c..9574994bac8 100644 --- a/crypto/keys/secp256r1/pubkey.go +++ b/crypto/keys/secp256r1/pubkey.go @@ -1,6 +1,8 @@ package secp256r1 import ( + "encoding/base64" + tmcrypto "github.com/cometbft/cometbft/crypto" "github.com/cosmos/gogoproto/proto" @@ -8,6 +10,20 @@ import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" ) +// customProtobufType is here to make sure that ecdsaPK and ecdsaSK implement the +// gogoproto customtype interface. +type customProtobufType interface { + Marshal() ([]byte, error) + MarshalTo(data []byte) (n int, err error) + Unmarshal(data []byte) error + Size() int + + MarshalJSON() ([]byte, error) + UnmarshalJSON(data []byte) error +} + +var _ customProtobufType = (*ecdsaPK)(nil) + // String implements proto.Message interface. func (m *PubKey) String() string { return m.Key.String(name) @@ -49,6 +65,27 @@ type ecdsaPK struct { ecdsa.PubKey } +// Marshal implements customProtobufType. +func (pk ecdsaPK) Marshal() ([]byte, error) { + return pk.PubKey.Bytes(), nil +} + +// MarshalJSON implements customProtobufType. +func (pk ecdsaPK) MarshalJSON() ([]byte, error) { + b64 := base64.StdEncoding.EncodeToString(pk.PubKey.Bytes()) + return []byte(b64), nil +} + +// UnmarshalJSON implements customProtobufType. +func (pk *ecdsaPK) UnmarshalJSON(data []byte) error { + bz, err := base64.StdEncoding.DecodeString(string(data)) + if err != nil { + return err + } + + return pk.PubKey.Unmarshal(bz, secp256r1, pubKeySize) +} + // Size implements proto.Marshaler interface func (pk *ecdsaPK) Size() int { if pk == nil { diff --git a/crypto/keys/secp256r1/pubkey_internal_test.go b/crypto/keys/secp256r1/pubkey_internal_test.go index eecbbb24d7d..f29eb15853d 100644 --- a/crypto/keys/secp256r1/pubkey_internal_test.go +++ b/crypto/keys/secp256r1/pubkey_internal_test.go @@ -126,3 +126,14 @@ func (suite *PKSuite) TestSize() { var nilPk *ecdsaPK require.Equal(0, nilPk.Size(), "nil value must have zero size") } + +func (suite *PKSuite) TestJson() { + require := suite.Require() + + bz, err := suite.pk.Key.MarshalJSON() + require.NoError(err) + + pk := &ecdsaPK{} + require.NoError(pk.UnmarshalJSON(bz)) + require.Equal(suite.pk.Key, pk) +}