From b4f8377a085d16ebb412dd7528130ccc6321e8a6 Mon Sep 17 00:00:00 2001 From: georgehao Date: Fri, 12 Apr 2024 07:19:31 +0800 Subject: [PATCH] fix(coordinator): fix coordinator recover public key inconsistent (#1265) Co-authored-by: georgehao --- common/types/message/auth_msg.go | 91 +++++++++++++++++++++ common/types/message/legacy_auth_msg.go | 89 ++++++++++++++++++++ common/types/message/message.go | 81 ------------------ common/version/version.go | 2 +- coordinator/internal/controller/api/auth.go | 33 +++++--- coordinator/test/mock_prover.go | 35 +++++--- 6 files changed, 228 insertions(+), 103 deletions(-) create mode 100644 common/types/message/auth_msg.go create mode 100644 common/types/message/legacy_auth_msg.go diff --git a/common/types/message/auth_msg.go b/common/types/message/auth_msg.go new file mode 100644 index 000000000..664f497ea --- /dev/null +++ b/common/types/message/auth_msg.go @@ -0,0 +1,91 @@ +package message + +import ( + "crypto/ecdsa" + + "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/common/hexutil" + "github.com/scroll-tech/go-ethereum/crypto" + "github.com/scroll-tech/go-ethereum/rlp" +) + +// AuthMsg is the first message exchanged from the Prover to the Sequencer. +// It effectively acts as a registration, and makes the Prover identification +// known to the Sequencer. +type AuthMsg struct { + // Message fields + Identity *Identity `json:"message"` + // Prover signature + Signature string `json:"signature"` +} + +// Identity contains all the fields to be signed by the prover. +type Identity struct { + // ProverName the prover name + ProverName string `json:"prover_name"` + // ProverVersion the prover version + ProverVersion string `json:"prover_version"` + // Challenge unique challenge generated by manager + Challenge string `json:"challenge"` + // HardForkName the hard fork name + HardForkName string `json:"hard_fork_name"` +} + +// SignWithKey auth message with private key and set public key in auth message's Identity +func (a *AuthMsg) SignWithKey(priv *ecdsa.PrivateKey) error { + // Hash identity content + hash, err := a.Identity.Hash() + if err != nil { + return err + } + + // Sign register message + sig, err := crypto.Sign(hash, priv) + if err != nil { + return err + } + a.Signature = hexutil.Encode(sig) + + return nil +} + +// Verify verifies the message of auth. +func (a *AuthMsg) Verify() (bool, error) { + hash, err := a.Identity.Hash() + if err != nil { + return false, err + } + sig := common.FromHex(a.Signature) + + pk, err := crypto.SigToPub(hash, sig) + if err != nil { + return false, err + } + return crypto.VerifySignature(crypto.CompressPubkey(pk), hash, sig[:len(sig)-1]), nil +} + +// PublicKey return public key from signature +func (a *AuthMsg) PublicKey() (string, error) { + hash, err := a.Identity.Hash() + if err != nil { + return "", err + } + sig := common.FromHex(a.Signature) + // recover public key + pk, err := crypto.SigToPub(hash, sig) + if err != nil { + return "", err + } + return common.Bytes2Hex(crypto.CompressPubkey(pk)), nil +} + +// Hash returns the hash of the auth message, which should be the message used +// to construct the Signature. +func (i *Identity) Hash() ([]byte, error) { + byt, err := rlp.EncodeToBytes(i) + if err != nil { + return nil, err + } + hash := crypto.Keccak256Hash(byt) + return hash[:], nil +} diff --git a/common/types/message/legacy_auth_msg.go b/common/types/message/legacy_auth_msg.go new file mode 100644 index 000000000..1ba2b40cc --- /dev/null +++ b/common/types/message/legacy_auth_msg.go @@ -0,0 +1,89 @@ +package message + +import ( + "crypto/ecdsa" + + "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/common/hexutil" + "github.com/scroll-tech/go-ethereum/crypto" + "github.com/scroll-tech/go-ethereum/rlp" +) + +// LegacyAuthMsg is the old auth message exchanged from the Prover to the Sequencer. +// It effectively acts as a registration, and makes the Prover identification +// known to the Sequencer. +type LegacyAuthMsg struct { + // Message fields + Identity *LegacyIdentity `json:"message"` + // Prover signature + Signature string `json:"signature"` +} + +// LegacyIdentity contains all the fields to be signed by the prover. +type LegacyIdentity struct { + // ProverName the prover name + ProverName string `json:"prover_name"` + // ProverVersion the prover version + ProverVersion string `json:"prover_version"` + // Challenge unique challenge generated by manager + Challenge string `json:"challenge"` +} + +// SignWithKey auth message with private key and set public key in auth message's Identity +func (a *LegacyAuthMsg) SignWithKey(priv *ecdsa.PrivateKey) error { + // Hash identity content + hash, err := a.Identity.Hash() + if err != nil { + return err + } + + // Sign register message + sig, err := crypto.Sign(hash, priv) + if err != nil { + return err + } + a.Signature = hexutil.Encode(sig) + + return nil +} + +// Verify verifies the message of auth. +func (a *LegacyAuthMsg) Verify() (bool, error) { + hash, err := a.Identity.Hash() + if err != nil { + return false, err + } + sig := common.FromHex(a.Signature) + + pk, err := crypto.SigToPub(hash, sig) + if err != nil { + return false, err + } + return crypto.VerifySignature(crypto.CompressPubkey(pk), hash, sig[:len(sig)-1]), nil +} + +// PublicKey return public key from signature +func (a *LegacyAuthMsg) PublicKey() (string, error) { + hash, err := a.Identity.Hash() + if err != nil { + return "", err + } + sig := common.FromHex(a.Signature) + // recover public key + pk, err := crypto.SigToPub(hash, sig) + if err != nil { + return "", err + } + return common.Bytes2Hex(crypto.CompressPubkey(pk)), nil +} + +// Hash returns the hash of the auth message, which should be the message used +// to construct the Signature. +func (i *LegacyIdentity) Hash() ([]byte, error) { + byt, err := rlp.EncodeToBytes(i) + if err != nil { + return nil, err + } + hash := crypto.Keccak256Hash(byt) + return hash[:], nil +} diff --git a/common/types/message/message.go b/common/types/message/message.go index 408258493..9f66ee50d 100644 --- a/common/types/message/message.go +++ b/common/types/message/message.go @@ -58,28 +58,6 @@ const ( ProofTypeBatch ) -// AuthMsg is the first message exchanged from the Prover to the Sequencer. -// It effectively acts as a registration, and makes the Prover identification -// known to the Sequencer. -type AuthMsg struct { - // Message fields - Identity *Identity `json:"message"` - // Prover signature - Signature string `json:"signature"` -} - -// Identity contains all the fields to be signed by the prover. -type Identity struct { - // ProverName the prover name - ProverName string `json:"prover_name"` - // ProverVersion the prover version - ProverVersion string `json:"prover_version"` - // Challenge unique challenge generated by manager - Challenge string `json:"challenge"` - // HardForkName the hard fork name - HardForkName string `json:"hard_fork_name"` -} - // GenerateToken generates token func GenerateToken() (string, error) { b := make([]byte, 16) @@ -89,65 +67,6 @@ func GenerateToken() (string, error) { return hex.EncodeToString(b), nil } -// SignWithKey auth message with private key and set public key in auth message's Identity -func (a *AuthMsg) SignWithKey(priv *ecdsa.PrivateKey) error { - // Hash identity content - hash, err := a.Identity.Hash() - if err != nil { - return err - } - - // Sign register message - sig, err := crypto.Sign(hash, priv) - if err != nil { - return err - } - a.Signature = hexutil.Encode(sig) - - return nil -} - -// Verify verifies the message of auth. -func (a *AuthMsg) Verify() (bool, error) { - hash, err := a.Identity.Hash() - if err != nil { - return false, err - } - sig := common.FromHex(a.Signature) - - pk, err := crypto.SigToPub(hash, sig) - if err != nil { - return false, err - } - return crypto.VerifySignature(crypto.CompressPubkey(pk), hash, sig[:len(sig)-1]), nil -} - -// PublicKey return public key from signature -func (a *AuthMsg) PublicKey() (string, error) { - hash, err := a.Identity.Hash() - if err != nil { - return "", err - } - sig := common.FromHex(a.Signature) - // recover public key - pk, err := crypto.SigToPub(hash, sig) - if err != nil { - return "", err - } - return common.Bytes2Hex(crypto.CompressPubkey(pk)), nil -} - -// Hash returns the hash of the auth message, which should be the message used -// to construct the Signature. -func (i *Identity) Hash() ([]byte, error) { - byt, err := rlp.EncodeToBytes(i) - if err != nil { - return nil, err - } - hash := crypto.Keccak256Hash(byt) - return hash[:], nil -} - // ProofMsg is the data structure sent to the coordinator. type ProofMsg struct { *ProofDetail `json:"zkProof"` diff --git a/common/version/version.go b/common/version/version.go index f5e330620..2ceff68fc 100644 --- a/common/version/version.go +++ b/common/version/version.go @@ -5,7 +5,7 @@ import ( "runtime/debug" ) -var tag = "v4.3.88" +var tag = "v4.3.89" var commit = func() string { if info, ok := debug.ReadBuildInfo(); ok { diff --git a/coordinator/internal/controller/api/auth.go b/coordinator/internal/controller/api/auth.go index f3d3d7aa1..a071b5321 100644 --- a/coordinator/internal/controller/api/auth.go +++ b/coordinator/internal/controller/api/auth.go @@ -53,18 +53,31 @@ func (a *AuthController) PayloadFunc(data interface{}) jwt.MapClaims { return jwt.MapClaims{} } - // recover the public key - authMsg := message.AuthMsg{ - Identity: &message.Identity{ - Challenge: v.Message.Challenge, - ProverName: v.Message.ProverName, - ProverVersion: v.Message.ProverVersion, - HardForkName: v.Message.HardForkName, - }, - Signature: v.Signature, + var publicKey string + var err error + if v.Message.HardForkName != "" { + authMsg := message.AuthMsg{ + Identity: &message.Identity{ + Challenge: v.Message.Challenge, + ProverName: v.Message.ProverName, + ProverVersion: v.Message.ProverVersion, + HardForkName: v.Message.HardForkName, + }, + Signature: v.Signature, + } + publicKey, err = authMsg.PublicKey() + } else { + authMsg := message.LegacyAuthMsg{ + Identity: &message.LegacyIdentity{ + Challenge: v.Message.Challenge, + ProverName: v.Message.ProverName, + ProverVersion: v.Message.ProverVersion, + }, + Signature: v.Signature, + } + publicKey, err = authMsg.PublicKey() } - publicKey, err := authMsg.PublicKey() if err != nil { return jwt.MapClaims{} } diff --git a/coordinator/test/mock_prover.go b/coordinator/test/mock_prover.go index dfad28b9b..67ce32b3a 100644 --- a/coordinator/test/mock_prover.go +++ b/coordinator/test/mock_prover.go @@ -77,18 +77,31 @@ func (r *mockProver) challenge(t *testing.T) string { } func (r *mockProver) login(t *testing.T, challengeString string, forkName string) string { - authMsg := message.AuthMsg{ - Identity: &message.Identity{ - Challenge: challengeString, - ProverName: r.proverName, - ProverVersion: r.proverVersion, - HardForkName: forkName, - }, + var body string + if forkName != "" { + authMsg := message.AuthMsg{ + Identity: &message.Identity{ + Challenge: challengeString, + ProverName: r.proverName, + ProverVersion: r.proverVersion, + HardForkName: forkName, + }, + } + assert.NoError(t, authMsg.SignWithKey(r.privKey)) + body = fmt.Sprintf("{\"message\":{\"challenge\":\"%s\",\"prover_name\":\"%s\", \"prover_version\":\"%s\", \"hard_fork_name\":\"%s\"},\"signature\":\"%s\"}", + authMsg.Identity.Challenge, authMsg.Identity.ProverName, authMsg.Identity.ProverVersion, authMsg.Identity.HardForkName, authMsg.Signature) + } else { + authMsg := message.LegacyAuthMsg{ + Identity: &message.LegacyIdentity{ + Challenge: challengeString, + ProverName: r.proverName, + ProverVersion: r.proverVersion, + }, + } + assert.NoError(t, authMsg.SignWithKey(r.privKey)) + body = fmt.Sprintf("{\"message\":{\"challenge\":\"%s\",\"prover_name\":\"%s\", \"prover_version\":\"%s\"},\"signature\":\"%s\"}", + authMsg.Identity.Challenge, authMsg.Identity.ProverName, authMsg.Identity.ProverVersion, authMsg.Signature) } - assert.NoError(t, authMsg.SignWithKey(r.privKey)) - - body := fmt.Sprintf("{\"message\":{\"challenge\":\"%s\",\"prover_name\":\"%s\", \"prover_version\":\"%s\", \"hard_fork_name\":\"%s\"},\"signature\":\"%s\"}", - authMsg.Identity.Challenge, authMsg.Identity.ProverName, authMsg.Identity.ProverVersion, authMsg.Identity.HardForkName, authMsg.Signature) var result ctypes.Response client := resty.New()