diff --git a/abci/example/kvstore/persistent_kvstore.go b/abci/example/kvstore/persistent_kvstore.go index e06bfcff855..1d38eff6172 100644 --- a/abci/example/kvstore/persistent_kvstore.go +++ b/abci/example/kvstore/persistent_kvstore.go @@ -236,7 +236,7 @@ func isValidatorTx(tx []byte) bool { } // format is "val:pubkey!power" -// pubkey is a base64-encoded 32-byte ed25519 key +// pubkey is a base64-encoded 32-byte ed25519 key or sm2 key func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.ResponseDeliverTx { tx = tx[len(ValidatorSetChangePrefix):] diff --git a/abci/types/pubkey.go b/abci/types/pubkey.go index 8341c0898d3..0f6670fd51b 100644 --- a/abci/types/pubkey.go +++ b/abci/types/pubkey.go @@ -6,6 +6,7 @@ import ( "github.com/cometbft/cometbft/crypto/ed25519" cryptoenc "github.com/cometbft/cometbft/crypto/encoding" "github.com/cometbft/cometbft/crypto/secp256k1" + "github.com/cometbft/cometbft/crypto/sm2" ) func Ed25519ValidatorUpdate(pk []byte, power int64) ValidatorUpdate { @@ -38,6 +39,18 @@ func UpdateValidator(pk []byte, power int64, keyType string) ValidatorUpdate { PubKey: pkp, Power: power, } + case sm2.KeyType: + var pke sm2.PubKeySm2 + copy(pke[:], pk) + pkp, err := cryptoenc.PubKeyToProto(pke) + if err != nil { + panic(err) + } + return ValidatorUpdate{ + // Address: + PubKey: pkp, + Power: power, + } default: panic(fmt.Sprintf("key type %s not supported", keyType)) } diff --git a/cmd/priv_val_server/main.go b/cmd/priv_val_server/main.go index fa2edc9342f..940cfe96cbc 100644 --- a/cmd/priv_val_server/main.go +++ b/cmd/priv_val_server/main.go @@ -5,7 +5,7 @@ import ( "os" "time" - "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/crypto/algo" "github.com/cometbft/cometbft/libs/log" cmtnet "github.com/cometbft/cometbft/libs/net" cmtos "github.com/cometbft/cometbft/libs/os" @@ -43,7 +43,7 @@ func main() { dialer = privval.DialUnixFn(address) case "tcp": connTimeout := 3 * time.Second // TODO - dialer = privval.DialTCPFn(address, connTimeout, ed25519.GenPrivKey()) + dialer = privval.DialTCPFn(address, connTimeout, algo.GenPrivKey()) default: logger.Error("Unknown protocol", "protocol", protocol) os.Exit(1) diff --git a/crypto/algo/algo.go b/crypto/algo/algo.go new file mode 100644 index 00000000000..9bb4b5bec52 --- /dev/null +++ b/crypto/algo/algo.go @@ -0,0 +1,175 @@ +package algo + +import ( + "github.com/cometbft/cometbft/crypto" + "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/crypto/sm2" +) + +const ( + ED25519 = "ed25519" + SM2 = "sm2" +) + +var Algo = "ed25519" + +func GetPubKeyType() string { + switch Algo { + case ED25519: + return ed25519.KeyType + + case SM2: + return sm2.KeyType + + default: + return ed25519.KeyType + } +} + +func GetPrivKeyBytes(privKey crypto.PrivKey) []byte { + switch Algo { + case ED25519: + key := privKey.(ed25519.PrivKey) + return key[:] + + case SM2: + key := privKey.(sm2.PrivKeySm2) + return key[:] + + default: + key := privKey.(ed25519.PrivKey) + return key[:] + } +} + +func GetPubKeyBytes(pubKey crypto.PubKey) []byte { + switch Algo { + case ED25519: + key := pubKey.(ed25519.PubKey) + return key[:] + + case SM2: + key := pubKey.(sm2.PubKeySm2) + return key[:] + + default: + key := pubKey.(ed25519.PubKey) + return key[:] + } +} + +func GetPubKeyFromData(keyType string, keyData []byte) crypto.PubKey { + switch Algo { + case ED25519: + pubkey := ed25519.PubKey{} + copy(pubkey[:], keyData) + return pubkey + + case SM2: + pubkey := sm2.PubKeySm2{} + copy(pubkey[:], keyData) + return pubkey + + default: + pubkey := ed25519.PubKey{} + copy(pubkey[:], keyData) + return pubkey + } +} + +func GenPrivKey() crypto.PrivKey { + switch Algo { + case ED25519: + return ed25519.GenPrivKey() + + case SM2: + return sm2.GenPrivKey() + + default: + return ed25519.GenPrivKey() + } +} + +func GenPrivKeyFromSecret(secret []byte) crypto.PrivKey { + switch Algo { + case ED25519: + return ed25519.GenPrivKeyFromSecret(secret) + + case SM2: + return sm2.GenPrivKeySm2FromSecret(secret) + + default: + return ed25519.GenPrivKeyFromSecret(secret) + } +} + +func GetPrivKeySize() int { + switch Algo { + case ED25519: + return 64 + + case SM2: + return sm2.PrivKeySize + + default: + return 64 + } +} + +func GetPubKeySize() int { + switch Algo { + case ED25519: + return ed25519.PubKeySize + + case SM2: + return sm2.PubKeySize + + default: + return ed25519.PubKeySize + } +} + +func GetSignatureSize() int { + switch Algo { + case ED25519: + return ed25519.SignatureSize + + case SM2: + return sm2.SignatureSize + + default: + return ed25519.SignatureSize + } +} + +func VerifyPubKeyType(pubKey crypto.PubKey) bool { + switch Algo { + case ED25519: + if _, ok := pubKey.(ed25519.PubKey); ok { + return true + } + + case SM2: + if _, ok := pubKey.(sm2.PubKeySm2); ok { + return true + } + } + + return false +} + +func VerifyPrivKeyType(privKey crypto.PrivKey) bool { + switch Algo { + case ED25519: + if _, ok := privKey.(ed25519.PrivKey); ok { + return true + } + + case SM2: + if _, ok := privKey.(sm2.PrivKeySm2); ok { + return true + } + } + + return false +} diff --git a/crypto/encoding/codec.go b/crypto/encoding/codec.go index 5980f88d4d5..40ee53ed329 100644 --- a/crypto/encoding/codec.go +++ b/crypto/encoding/codec.go @@ -6,6 +6,7 @@ import ( "github.com/cometbft/cometbft/crypto" "github.com/cometbft/cometbft/crypto/ed25519" "github.com/cometbft/cometbft/crypto/secp256k1" + "github.com/cometbft/cometbft/crypto/sm2" "github.com/cometbft/cometbft/libs/json" pc "github.com/cometbft/cometbft/proto/tendermint/crypto" ) @@ -32,6 +33,12 @@ func PubKeyToProto(k crypto.PubKey) (pc.PublicKey, error) { Secp256K1: k, }, } + case sm2.PubKeySm2: + kp = pc.PublicKey{ + Sum: &pc.PublicKey_Sm2{ + Sm2: k[:], + }, + } default: return kp, fmt.Errorf("toproto: key type %v is not supported", k) } @@ -57,6 +64,14 @@ func PubKeyFromProto(k pc.PublicKey) (crypto.PubKey, error) { pk := make(secp256k1.PubKey, secp256k1.PubKeySize) copy(pk, k.Secp256K1) return pk, nil + case *pc.PublicKey_Sm2: + if len(k.Sm2) != sm2.PubKeySize { + return nil, fmt.Errorf("invalid size for PubKeySm2. Got %d, expected %d", + len(k.Sm2), sm2.PubKeySize) + } + pk := sm2.PubKeySm2{} + copy(pk[:], k.Sm2) + return pk, nil default: return nil, fmt.Errorf("fromproto: key type %v is not supported", k) } diff --git a/crypto/sm2/sm2.go b/crypto/sm2/sm2.go new file mode 100644 index 00000000000..0983cd903bc --- /dev/null +++ b/crypto/sm2/sm2.go @@ -0,0 +1,204 @@ +package sm2 + +import ( + "bytes" + "crypto/rand" + "crypto/sha256" + "crypto/subtle" + "fmt" + + "io" + "math/big" + + "github.com/tjfoc/gmsm/sm2" + + "github.com/cometbft/cometbft/crypto" + "github.com/cometbft/cometbft/crypto/tmhash" + ctmjson "github.com/cometbft/cometbft/libs/json" +) + +const ( + PrivKeyName = "tendermint/PrivKeySm2" + PubKeyName = "tendermint/PubKeySm2" + + PrivKeySize = 32 + PubKeySize = 33 + SignatureSize = 64 + + KeyType = "sm2" +) + +func init() { + ctmjson.RegisterType(PubKeySm2{}, PubKeyName) + ctmjson.RegisterType(PrivKeySm2{}, PrivKeyName) +} + +type PrivKeySm2 [PrivKeySize]byte + +// Bytes returns the privkey byte format. +func (privKey PrivKeySm2) Bytes() []byte { + return privKey[:] +} + +type PubKeySm2 [PubKeySize]byte + +func (pubKey PubKeySm2) Type() string { + return KeyType +} + +var _ crypto.PrivKey = PrivKeySm2{} +var _ crypto.PubKey = PubKeySm2{} + +func (privKey PrivKeySm2) Sign(msg []byte) ([]byte, error) { + priv := privKey.GetPrivateKey() + r, s, err := sm2.Sm2Sign(priv, msg, nil, rand.Reader) + if err != nil { + return nil, err + } + R := r.Bytes() + S := s.Bytes() + sig := make([]byte, 64) + copy(sig[32-len(R):32], R[:]) + copy(sig[64-len(S):64], S[:]) + + return sig, nil +} + +func (privKey PrivKeySm2) Sm2Sign(msg []byte) ([]byte, error) { + r, s, err := sm2.Sm2Sign(privKey.GetPrivateKey(), msg, nil, rand.Reader) + if err != nil { + return nil, err + } + + R := r.Bytes() + S := s.Bytes() + sig := make([]byte, 64) + copy(sig[32-len(R):32], R[:]) + copy(sig[64-len(S):64], S[:]) + + return sig, nil +} + +func (privKey PrivKeySm2) PubKey() crypto.PubKey { + priv := privKey.GetPrivateKey() + compPubkey := sm2.Compress(&priv.PublicKey) + var pubKey PubKeySm2 + copy(pubKey[:], compPubkey) + + return pubKey +} + +func (privKey PrivKeySm2) PubKeySm2() PubKeySm2 { + priv := privKey.GetPrivateKey() + compPubkey := sm2.Compress(&priv.PublicKey) + + var pubKey PubKeySm2 + copy(pubKey[:], compPubkey) + + return pubKey +} + +func (privKey PrivKeySm2) Equals(other crypto.PrivKey) bool { + if otherSm2, ok := other.(PrivKeySm2); ok { + return subtle.ConstantTimeCompare(privKey[:], otherSm2[:]) == 1 + } + + return false +} + +func (privKey PrivKeySm2) Type() string { + return KeyType +} + +func (privKey PrivKeySm2) GetPrivateKey() *sm2.PrivateKey { + k := new(big.Int).SetBytes(privKey[:32]) + c := sm2.P256Sm2() + priv := new(sm2.PrivateKey) + priv.PublicKey.Curve = c + priv.D = k + priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes()) + + return priv +} + +func GenPrivKey() PrivKeySm2 { + return genPrivKey(crypto.CReader()) +} + +func genPrivKey(rand io.Reader) PrivKeySm2 { + seed := make([]byte, 32) + if _, err := io.ReadFull(rand, seed); err != nil { + panic(err) + } + + privKey, err := sm2.GenerateKey(rand) + if err != nil { + panic(err) + } + + var privKeySm2 PrivKeySm2 + copy(privKeySm2[:], privKey.D.Bytes()) + + return privKeySm2 +} + +func GenPrivKeySm2FromSecret(secret []byte) PrivKeySm2 { + one := new(big.Int).SetInt64(1) + secHash := sha256.Sum256(secret) + + k := new(big.Int).SetBytes(secHash[:]) + n := new(big.Int).Sub(sm2.P256Sm2().Params().N, one) + k.Mod(k, n) + k.Add(k, one) + + var privKeySm2 PrivKeySm2 + copy(privKeySm2[:], k.Bytes()) + + return privKeySm2 +} + +// -------------------------------------------------------- + +func (pubKey PubKeySm2) Address() crypto.Address { + return crypto.Address(tmhash.SumTruncated(pubKey[:])) +} + +func (pubKey PubKeySm2) Bytes() []byte { + return pubKey[:] +} + +func (pubKey PubKeySm2) VerifySignature(msg []byte, sig []byte) bool { + if len(sig) != SignatureSize { + return false + } + + publicKey := sm2.Decompress(pubKey[:]) + r := new(big.Int).SetBytes(sig[:32]) + s := new(big.Int).SetBytes(sig[32:]) + + return sm2.Sm2Verify(publicKey, msg, nil, r, s) +} + +func (pubKey PubKeySm2) Sm2VerifyBytes(msg []byte, sig []byte) bool { + if len(sig) != SignatureSize { + return false + } + + publicKey := sm2.Decompress(pubKey[:]) + r := new(big.Int).SetBytes(sig[:32]) + s := new(big.Int).SetBytes(sig[32:]) + + return sm2.Sm2Verify(publicKey, msg, nil, r, s) +} + +func (pubKey PubKeySm2) String() string { + return fmt.Sprintf("PubKeySm2{%X}", pubKey[:]) +} + +func (pubKey PubKeySm2) Equals(other crypto.PubKey) bool { + if otherSm2, ok := other.(PubKeySm2); ok { + return bytes.Equal(pubKey[:], otherSm2[:]) + } + + return false +} diff --git a/crypto/sm2/sm2_bench_test.go b/crypto/sm2/sm2_bench_test.go new file mode 100644 index 00000000000..323f2bf6aad --- /dev/null +++ b/crypto/sm2/sm2_bench_test.go @@ -0,0 +1,26 @@ +package sm2 + +import ( + "io" + "testing" + + "github.com/cometbft/cometbft/crypto" + "github.com/cometbft/cometbft/crypto/internal/benchmarking" +) + +func BenchmarkKeyGeneration(b *testing.B) { + benchmarkKeygenWrapper := func(reader io.Reader) crypto.PrivKey { + return genPrivKey(reader) + } + benchmarking.BenchmarkKeyGeneration(b, benchmarkKeygenWrapper) +} + +func BenchmarkSigning(b *testing.B) { + priv := GenPrivKey() + benchmarking.BenchmarkSigning(b, priv) +} + +func BenchmarkVerification(b *testing.B) { + priv := GenPrivKey() + benchmarking.BenchmarkVerification(b, priv) +} diff --git a/crypto/sm2/sm2_test.go b/crypto/sm2/sm2_test.go new file mode 100644 index 00000000000..61af8dd578d --- /dev/null +++ b/crypto/sm2/sm2_test.go @@ -0,0 +1,66 @@ +package sm2_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/cometbft/cometbft/crypto" + "github.com/cometbft/cometbft/crypto/sm2" +) + +// nolint +func TestSignAndValidate(t *testing.T) { + for i := 0; i < 1000; i++ { + privKey := sm2.GenPrivKey() + pubKey := privKey.PubKey() + + msg := crypto.CRandBytes(128) + sig, err := privKey.Sign(msg) + require.Nil(t, err) + + // Test the signature + if !pubKey.VerifySignature(msg, sig) { + fmt.Printf("# %d: Verify error\n", i) + } + + // Mutate the signature, just one bit. + sig[7] ^= byte(0x01) + + assert.False(t, pubKey.VerifySignature(msg, sig)) + } + +} + +func TestSm2SignAndSm2Validate(t *testing.T) { + for i := 0; i < 1000; i++ { + privKey := sm2.GenPrivKey() + pubKey := privKey.PubKey().(sm2.PubKeySm2) + + msg := crypto.CRandBytes(128) + sig, err := privKey.Sm2Sign(msg) + require.Nil(t, err) + + // Test the signature + if !pubKey.Sm2VerifyBytes(msg, sig) { + fmt.Printf("# %d: Verify error\n", i) + } + + // Mutate the signature, just one bit. + sig[7] ^= byte(0x01) + + assert.False(t, pubKey.VerifySignature(msg, sig)) + } + +} + +func TestGenPrivKeySm2FromSecret(t *testing.T) { + a := sm2.GenPrivKeySm2FromSecret([]byte("mySecret1")) + b := sm2.GenPrivKeySm2FromSecret([]byte("mySecret1")) + c := sm2.GenPrivKeySm2FromSecret([]byte("mySecret2")) + + require.Equal(t, a, b) + require.NotEqual(t, a, c) +} diff --git a/go.mod b/go.mod index bb73b433cc3..88359168312 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,7 @@ require ( github.com/spf13/cobra v1.6.1 github.com/spf13/viper v1.13.0 github.com/stretchr/testify v1.8.1 + github.com/tjfoc/gmsm v1.4.0 golang.org/x/crypto v0.5.0 golang.org/x/net v0.7.0 google.golang.org/grpc v1.52.0 diff --git a/go.sum b/go.sum index 0a96d6de643..b8009ef077c 100644 --- a/go.sum +++ b/go.sum @@ -861,6 +861,8 @@ github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144 h1:kl4KhGNsJIbDH github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= github.com/timonwong/loggercheck v0.9.3 h1:ecACo9fNiHxX4/Bc02rW2+kaJIAMAes7qJ7JKxt0EZI= github.com/timonwong/loggercheck v0.9.3/go.mod h1:wUqnk9yAOIKtGA39l1KLE9Iz0QiTocu/YZoOf+OzFdw= +github.com/tjfoc/gmsm v1.4.0 h1:8nbaiZG+iVdh+fXVw0DZoZZa7a4TGm3Qab+xdrdzj8s= +github.com/tjfoc/gmsm v1.4.0/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/tomarrell/wrapcheck/v2 v2.7.0 h1:J/F8DbSKJC83bAvC6FoZaRjZiZ/iKoueSdrEkmGeacA= github.com/tomarrell/wrapcheck/v2 v2.7.0/go.mod h1:ao7l5p0aOlUNJKI0qVwB4Yjlqutd0IvAB9Rdwyilxvg= github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw= @@ -932,6 +934,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -1021,6 +1024,7 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= diff --git a/libs/bytes/bytes.go b/libs/bytes/bytes.go index 95b4cc35fca..6877718748a 100644 --- a/libs/bytes/bytes.go +++ b/libs/bytes/bytes.go @@ -63,3 +63,8 @@ func (bz HexBytes) Format(s fmt.State, verb rune) { s.Write([]byte(fmt.Sprintf("%X", []byte(bz)))) } } + +// MarshalYAML returns the YAML representation +func (bz HexBytes) MarshalYAML() (interface{}, error) { + return bz.String(), nil +} diff --git a/p2p/conn/secret_connection.go b/p2p/conn/secret_connection.go index a6de1df07f5..4dfbc354f13 100644 --- a/p2p/conn/secret_connection.go +++ b/p2p/conn/secret_connection.go @@ -22,7 +22,7 @@ import ( "golang.org/x/crypto/nacl/box" "github.com/cometbft/cometbft/crypto" - "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/crypto/algo" cryptoenc "github.com/cometbft/cometbft/crypto/encoding" "github.com/cometbft/cometbft/libs/async" "github.com/cometbft/cometbft/libs/protoio" @@ -167,8 +167,8 @@ func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKey) (* } remPubKey, remSignature := authSigMsg.Key, authSigMsg.Sig - if _, ok := remPubKey.(ed25519.PubKey); !ok { - return nil, fmt.Errorf("expected ed25519 pubkey, got %T", remPubKey) + if !algo.VerifyPubKeyType(remPubKey) { + return nil, fmt.Errorf("expected %s pubkey, got %T", algo.Algo, remPubKey) } if !remPubKey.VerifySignature(challenge[:], remSignature) { return nil, errors.New("challenge verification failed") diff --git a/p2p/key.go b/p2p/key.go index b30dca1dcde..4e1d051c4bd 100644 --- a/p2p/key.go +++ b/p2p/key.go @@ -7,7 +7,7 @@ import ( "os" "github.com/cometbft/cometbft/crypto" - "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/crypto/algo" cmtjson "github.com/cometbft/cometbft/libs/json" cmtos "github.com/cometbft/cometbft/libs/os" ) @@ -56,7 +56,7 @@ func LoadOrGenNodeKey(filePath string) (*NodeKey, error) { return nodeKey, nil } - privKey := ed25519.GenPrivKey() + privKey := algo.GenPrivKey() nodeKey := &NodeKey{ PrivKey: privKey, } diff --git a/p2p/mock/peer.go b/p2p/mock/peer.go index 664711dc63e..ca963494043 100644 --- a/p2p/mock/peer.go +++ b/p2p/mock/peer.go @@ -3,7 +3,7 @@ package mock import ( "net" - "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/crypto/algo" "github.com/cometbft/cometbft/libs/service" "github.com/cometbft/cometbft/p2p" "github.com/cometbft/cometbft/p2p/conn" @@ -27,7 +27,7 @@ func NewPeer(ip net.IP) *Peer { } else { netAddr = p2p.NewNetAddressIPPort(ip, 26656) } - nodeKey := p2p.NodeKey{PrivKey: ed25519.GenPrivKey()} + nodeKey := p2p.NodeKey{PrivKey: algo.GenPrivKey()} netAddr.ID = nodeKey.ID() mp := &Peer{ ip: ip, diff --git a/privval/file.go b/privval/file.go index 8527c60e01f..0091e1beff4 100644 --- a/privval/file.go +++ b/privval/file.go @@ -10,7 +10,7 @@ import ( "github.com/cosmos/gogoproto/proto" "github.com/cometbft/cometbft/crypto" - "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/crypto/algo" cmtbytes "github.com/cometbft/cometbft/libs/bytes" cmtjson "github.com/cometbft/cometbft/libs/json" cmtos "github.com/cometbft/cometbft/libs/os" @@ -170,7 +170,7 @@ func NewFilePV(privKey crypto.PrivKey, keyFilePath, stateFilePath string) *FileP // GenFilePV generates a new validator with randomly generated private key // and sets the filePaths, but does not call Save(). func GenFilePV(keyFilePath, stateFilePath string) *FilePV { - return NewFilePV(ed25519.GenPrivKey(), keyFilePath, stateFilePath) + return NewFilePV(algo.GenPrivKey(), keyFilePath, stateFilePath) } // LoadFilePV loads a FilePV from the filePaths. The FilePV handles double diff --git a/privval/socket_listeners.go b/privval/socket_listeners.go index 6d406bd6925..86ec744c1f6 100644 --- a/privval/socket_listeners.go +++ b/privval/socket_listeners.go @@ -4,7 +4,7 @@ import ( "net" "time" - "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/crypto" p2pconn "github.com/cometbft/cometbft/p2p/conn" ) @@ -44,7 +44,7 @@ var _ net.Listener = (*TCPListener)(nil) type TCPListener struct { *net.TCPListener - secretConnKey ed25519.PrivKey + secretConnKey crypto.PrivKey timeoutAccept time.Duration timeoutReadWrite time.Duration @@ -52,7 +52,7 @@ type TCPListener struct { // NewTCPListener returns a listener that accepts authenticated encrypted connections // using the given secretConnKey and the default timeout values. -func NewTCPListener(ln net.Listener, secretConnKey ed25519.PrivKey) *TCPListener { +func NewTCPListener(ln net.Listener, secretConnKey crypto.PrivKey) *TCPListener { return &TCPListener{ TCPListener: ln.(*net.TCPListener), secretConnKey: secretConnKey, diff --git a/privval/utils.go b/privval/utils.go index b7d84a9e928..4ce43a292a9 100644 --- a/privval/utils.go +++ b/privval/utils.go @@ -5,7 +5,7 @@ import ( "fmt" "net" - "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/crypto/algo" "github.com/cometbft/cometbft/libs/log" cmtnet "github.com/cometbft/cometbft/libs/net" ) @@ -39,7 +39,7 @@ func NewSignerListener(listenAddr string, logger log.Logger) (*SignerListenerEnd listener = NewUnixListener(ln) case "tcp": // TODO: persist this key so external signer can actually authenticate us - listener = NewTCPListener(ln, ed25519.GenPrivKey()) + listener = NewTCPListener(ln, algo.GenPrivKey()) default: return nil, fmt.Errorf( "wrong listen address: expected either 'tcp' or 'unix' protocols, got %s", diff --git a/proto/tendermint/crypto/keys.pb.go b/proto/tendermint/crypto/keys.pb.go index 0edb2269f53..a22eaef1ad5 100644 --- a/proto/tendermint/crypto/keys.pb.go +++ b/proto/tendermint/crypto/keys.pb.go @@ -30,6 +30,7 @@ type PublicKey struct { // // *PublicKey_Ed25519 // *PublicKey_Secp256K1 + // *PublicKey_Sm2 Sum isPublicKey_Sum `protobuf_oneof:"sum"` } @@ -80,9 +81,13 @@ type PublicKey_Ed25519 struct { type PublicKey_Secp256K1 struct { Secp256K1 []byte `protobuf:"bytes,2,opt,name=secp256k1,proto3,oneof" json:"secp256k1,omitempty"` } +type PublicKey_Sm2 struct { + Sm2 []byte `protobuf:"bytes,3,opt,name=sm2,proto3,oneof" json:"sm2,omitempty"` +} func (*PublicKey_Ed25519) isPublicKey_Sum() {} func (*PublicKey_Secp256K1) isPublicKey_Sum() {} +func (*PublicKey_Sm2) isPublicKey_Sum() {} func (m *PublicKey) GetSum() isPublicKey_Sum { if m != nil { @@ -105,11 +110,19 @@ func (m *PublicKey) GetSecp256K1() []byte { return nil } +func (m *PublicKey) GetSm2() []byte { + if x, ok := m.GetSum().(*PublicKey_Sm2); ok { + return x.Sm2 + } + return nil +} + // XXX_OneofWrappers is for the internal use of the proto package. func (*PublicKey) XXX_OneofWrappers() []interface{} { return []interface{}{ (*PublicKey_Ed25519)(nil), (*PublicKey_Secp256K1)(nil), + (*PublicKey_Sm2)(nil), } } @@ -120,20 +133,21 @@ func init() { func init() { proto.RegisterFile("tendermint/crypto/keys.proto", fileDescriptor_cb048658b234868c) } var fileDescriptor_cb048658b234868c = []byte{ - // 204 bytes of a gzipped FileDescriptorProto + // 217 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x29, 0x49, 0xcd, 0x4b, 0x49, 0x2d, 0xca, 0xcd, 0xcc, 0x2b, 0xd1, 0x4f, 0x2e, 0xaa, 0x2c, 0x28, 0xc9, 0xd7, 0xcf, 0x4e, 0xad, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x44, 0xc8, 0xea, 0x41, 0x64, 0xa5, - 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0xb2, 0xfa, 0x20, 0x16, 0x44, 0xa1, 0x52, 0x04, 0x17, 0x67, + 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0xb2, 0xfa, 0x20, 0x16, 0x44, 0xa1, 0x52, 0x0e, 0x17, 0x67, 0x40, 0x69, 0x52, 0x4e, 0x66, 0xb2, 0x77, 0x6a, 0xa5, 0x90, 0x14, 0x17, 0x7b, 0x6a, 0x8a, 0x91, 0xa9, 0xa9, 0xa1, 0xa5, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x8f, 0x07, 0x43, 0x10, 0x4c, 0x40, 0x48, 0x8e, 0x8b, 0xb3, 0x38, 0x35, 0xb9, 0xc0, 0xc8, 0xd4, 0x2c, 0xdb, 0x50, 0x82, 0x09, 0x2a, 0x8b, - 0x10, 0xb2, 0xe2, 0x78, 0xb1, 0x40, 0x9e, 0xf1, 0xc5, 0x42, 0x79, 0x46, 0x27, 0x56, 0x2e, 0xe6, - 0xe2, 0xd2, 0x5c, 0x27, 0xbf, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, - 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32, - 0x49, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x4d, 0x2d, - 0x49, 0x4a, 0x2b, 0x41, 0x30, 0x20, 0x4e, 0xc4, 0xf0, 0x5d, 0x12, 0x1b, 0x58, 0xc2, 0x18, 0x10, - 0x00, 0x00, 0xff, 0xff, 0xa3, 0xfb, 0xf7, 0x98, 0xf9, 0x00, 0x00, 0x00, + 0x10, 0x12, 0x12, 0xe2, 0x62, 0x2e, 0xce, 0x35, 0x92, 0x60, 0x86, 0xca, 0x80, 0x38, 0x56, 0x1c, + 0x2f, 0x16, 0xc8, 0x33, 0xbe, 0x58, 0x28, 0xcf, 0xe8, 0xc4, 0xca, 0xc5, 0x5c, 0x5c, 0x9a, 0xeb, + 0xe4, 0x77, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, + 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x26, 0xe9, 0x99, 0x25, + 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0xc9, 0xf9, 0xb9, 0xa9, 0x25, 0x49, 0x69, 0x25, + 0x08, 0x06, 0xc4, 0xd9, 0x18, 0x3e, 0x4e, 0x62, 0x03, 0x4b, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, + 0xff, 0x65, 0x4e, 0x18, 0x94, 0x0d, 0x01, 0x00, 0x00, } func (this *PublicKey) Compare(that interface{}) int { @@ -174,6 +188,8 @@ func (this *PublicKey) Compare(that interface{}) int { thisType = 0 case *PublicKey_Secp256K1: thisType = 1 + case *PublicKey_Sm2: + thisType = 2 default: panic(fmt.Sprintf("compare: unexpected type %T in oneof", this.Sum)) } @@ -183,6 +199,8 @@ func (this *PublicKey) Compare(that interface{}) int { that1Type = 0 case *PublicKey_Secp256K1: that1Type = 1 + case *PublicKey_Sm2: + that1Type = 2 default: panic(fmt.Sprintf("compare: unexpected type %T in oneof", that1.Sum)) } @@ -258,6 +276,36 @@ func (this *PublicKey_Secp256K1) Compare(that interface{}) int { } return 0 } +func (this *PublicKey_Sm2) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*PublicKey_Sm2) + if !ok { + that2, ok := that.(PublicKey_Sm2) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if c := bytes.Compare(this.Sm2, that1.Sm2); c != 0 { + return c + } + return 0 +} func (this *PublicKey) Equal(that interface{}) bool { if that == nil { return this == nil @@ -336,6 +384,30 @@ func (this *PublicKey_Secp256K1) Equal(that interface{}) bool { } return true } +func (this *PublicKey_Sm2) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*PublicKey_Sm2) + if !ok { + that2, ok := that.(PublicKey_Sm2) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !bytes.Equal(this.Sm2, that1.Sm2) { + return false + } + return true +} func (m *PublicKey) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -400,6 +472,22 @@ func (m *PublicKey_Secp256K1) MarshalToSizedBuffer(dAtA []byte) (int, error) { } return len(dAtA) - i, nil } +func (m *PublicKey_Sm2) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PublicKey_Sm2) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Sm2 != nil { + i -= len(m.Sm2) + copy(dAtA[i:], m.Sm2) + i = encodeVarintKeys(dAtA, i, uint64(len(m.Sm2))) + i-- + dAtA[i] = 0x1a + } + return len(dAtA) - i, nil +} func encodeVarintKeys(dAtA []byte, offset int, v uint64) int { offset -= sovKeys(v) base := offset @@ -447,6 +535,18 @@ func (m *PublicKey_Secp256K1) Size() (n int) { } return n } +func (m *PublicKey_Sm2) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sm2 != nil { + l = len(m.Sm2) + n += 1 + l + sovKeys(uint64(l)) + } + return n +} func sovKeys(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 @@ -549,6 +649,39 @@ func (m *PublicKey) Unmarshal(dAtA []byte) error { copy(v, dAtA[iNdEx:postIndex]) m.Sum = &PublicKey_Secp256K1{v} iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sm2", 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 + } + v := make([]byte, postIndex-iNdEx) + copy(v, dAtA[iNdEx:postIndex]) + m.Sum = &PublicKey_Sm2{v} + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipKeys(dAtA[iNdEx:]) diff --git a/proto/tendermint/crypto/keys.proto b/proto/tendermint/crypto/keys.proto index 8fa192fa4bc..e87aa524a78 100644 --- a/proto/tendermint/crypto/keys.proto +++ b/proto/tendermint/crypto/keys.proto @@ -13,5 +13,6 @@ message PublicKey { oneof sum { bytes ed25519 = 1; bytes secp256k1 = 2; + bytes sm2 = 3; } } diff --git a/types/params.go b/types/params.go index 35281998abb..66f8b218d4d 100644 --- a/types/params.go +++ b/types/params.go @@ -5,8 +5,11 @@ import ( "fmt" "time" + "github.com/cometbft/cometbft/crypto/algo" + "github.com/cometbft/cometbft/crypto/ed25519" "github.com/cometbft/cometbft/crypto/secp256k1" + "github.com/cometbft/cometbft/crypto/sm2" "github.com/cometbft/cometbft/crypto/tmhash" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" ) @@ -23,11 +26,13 @@ const ( ABCIPubKeyTypeEd25519 = ed25519.KeyType ABCIPubKeyTypeSecp256k1 = secp256k1.KeyType + ABCIPubKeyTypeSm2 = sm2.KeyType ) var ABCIPubKeyTypesToNames = map[string]string{ ABCIPubKeyTypeEd25519: ed25519.PubKeyName, ABCIPubKeyTypeSecp256k1: secp256k1.PubKeyName, + ABCIPubKeyTypeSm2: sm2.PubKeyName, } // ConsensusParams contains consensus critical parameters that determine the @@ -63,6 +68,17 @@ type VersionParams struct { App uint64 `json:"app"` } +func GetABCIPubKeyType() string { + switch algo.Algo { + case algo.ED25519: + return ABCIPubKeyTypeEd25519 + case algo.SM2: + return ABCIPubKeyTypeSm2 + default: + return ABCIPubKeyTypeEd25519 + } +} + // DefaultConsensusParams returns a default ConsensusParams. func DefaultConsensusParams() *ConsensusParams { return &ConsensusParams{ @@ -91,10 +107,10 @@ func DefaultEvidenceParams() EvidenceParams { } // DefaultValidatorParams returns a default ValidatorParams, which allows -// only ed25519 pubkeys. +// only ed25519 and sm2 pubkeys. func DefaultValidatorParams() ValidatorParams { return ValidatorParams{ - PubKeyTypes: []string{ABCIPubKeyTypeEd25519}, + PubKeyTypes: []string{GetABCIPubKeyType()}, } } diff --git a/types/priv_validator.go b/types/priv_validator.go index e4740c36c15..50b5693790d 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -6,7 +6,7 @@ import ( "fmt" "github.com/cometbft/cometbft/crypto" - "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/crypto/algo" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" ) @@ -54,7 +54,7 @@ type MockPV struct { } func NewMockPV() MockPV { - return MockPV{ed25519.GenPrivKey(), false, false} + return MockPV{algo.GenPrivKey(), false, false} } // NewMockPVWithParams allows one to create a MockPV instance, but with finer @@ -141,5 +141,5 @@ func (pv *ErroringMockPV) SignProposal(chainID string, proposal *cmtproto.Propos // NewErroringMockPV returns a MockPV that fails on each signing request. Again, for testing only. func NewErroringMockPV() *ErroringMockPV { - return &ErroringMockPV{MockPV{ed25519.GenPrivKey(), false, false}} + return &ErroringMockPV{MockPV{algo.GenPrivKey(), false, false}} } diff --git a/types/signable.go b/types/signable.go index 8ba2e6599a1..c7baa1da0d1 100644 --- a/types/signable.go +++ b/types/signable.go @@ -1,7 +1,7 @@ package types import ( - "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/crypto/algo" cmtmath "github.com/cometbft/cometbft/libs/math" ) @@ -9,7 +9,7 @@ var ( // MaxSignatureSize is a maximum allowed signature size for the Proposal // and Vote. // XXX: secp256k1 does not have Size nor MaxSize defined. - MaxSignatureSize = cmtmath.MaxInt(ed25519.SignatureSize, 64) + MaxSignatureSize = cmtmath.MaxInt(algo.GetSignatureSize(), 64) ) // Signable is an interface for all signable things.