Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(avs): prevent BLS12-381 replay attacks #314

Merged
merged 14 commits into from
Feb 24, 2025
Merged
10 changes: 6 additions & 4 deletions precompiles/avs/IAVSManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,16 @@ interface IAVSManager {
/// @param sender The external address for calling this method.
/// @param avsAddress The address of AVS.
/// @param pubKey the public keys of the operator
/// @param pubkeyRegistrationSignature the public keys of the operator
/// @param pubkeyRegistrationMessageHash the public keys of the operator
/// @param pubKeyRegistrationSignature the public keys of the operator
/// @param pubKeyRegistrationMessageHash the public keys of the operator
/// @param msg is the message which signed using a private key
function registerBLSPublicKey(
address sender,
address avsAddress,
bytes calldata pubKey,
bytes calldata pubkeyRegistrationSignature,
bytes calldata pubkeyRegistrationMessageHash
bytes calldata pubKeyRegistrationSignature,
bytes calldata pubKeyRegistrationMessageHash,
string msg
) external returns (bool success);

/// @dev operatorSubmitTask , this function enables a operator submit a task result.
Expand Down
9 changes: 7 additions & 2 deletions precompiles/avs/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,14 +314,19 @@ func (p Precompile) RegisterBLSPublicKey(
if !ok {
return nil, fmt.Errorf(exocmn.ErrContractInputParamOrType, 3, "[]byte", pubKeyRegistrationSignature)
}
blsParams.PubkeyRegistrationSignature = pubKeyRegistrationSignature
blsParams.PubKeyRegistrationSignature = pubKeyRegistrationSignature

pubKeyRegistrationMessageHash, ok := args[4].([]byte)
if !ok {
return nil, fmt.Errorf(exocmn.ErrContractInputParamOrType, 4, "[]byte", pubKeyRegistrationMessageHash)
}
blsParams.PubkeyRegistrationMessageHash = pubKeyRegistrationMessageHash
blsParams.PubKeyRegistrationMessageHash = pubKeyRegistrationMessageHash

msg, ok := args[5].(string)
if !ok {
return nil, fmt.Errorf(exocmn.ErrContractInputParamOrType, 5, "string", msg)
}
blsParams.Message = msg
err := p.avsKeeper.RegisterBLSPublicKey(ctx, blsParams)
if err != nil {
return nil, err
Expand Down
25 changes: 25 additions & 0 deletions x/avs/keeper/avs_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package keeper_test

import (
testutiltx "github.com/ExocoreNetwork/exocore/testutil/tx"
"github.com/ethereum/go-ethereum/crypto"
"github.com/prysmaticlabs/prysm/v4/crypto/bls/blst"
"math/big"
"strings"
"time"
Expand Down Expand Up @@ -195,3 +198,25 @@ func (suite *AVSTestSuite) TestAddressSwitch() {
commonAddress := common.Address(accAddress)
suite.Equal(common.HexToAddress("0x8dF46478a83Ab2a429979391E9546A12AfF9E33f"), commonAddress)
}

func (suite *AVSTestSuite) TestRegisterBLSPublicKey() {
privateKey, err := blst.RandKey()
suite.NoError(err)
publicKey := privateKey.PublicKey()
operatorAddress := sdk.AccAddress(utiltx.GenerateAddress().Bytes())
expectedMessage := "ExoCore-" + types.ChainIDWithoutRevision(suite.Ctx.ChainID()) + "-" + strings.ToLower(operatorAddress.String())
expectedHash := crypto.Keccak256Hash([]byte(expectedMessage))
sig := privateKey.Sign(expectedHash.Bytes())
params := &types.BlsParams{
OperatorAddress: operatorAddress,
AvsAddress: testutiltx.GenerateAddress(),
PubKey: publicKey.Marshal(),
PubKeyRegistrationSignature: sig.Marshal(),
PubKeyRegistrationMessageHash: expectedHash.Bytes(),
Message: expectedMessage,
}

err = suite.App.AVSManagerKeeper.RegisterBLSPublicKey(suite.Ctx, params)
suite.NoError(err)

}
28 changes: 22 additions & 6 deletions x/avs/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,24 @@ func (k Keeper) CreateAVSTask(ctx sdk.Context, params *types.TaskInfoParams) (ui

func (k Keeper) RegisterBLSPublicKey(ctx sdk.Context, params *types.BlsParams) error {
// check bls signature to prevent rogue key attacks
sig := params.PubkeyRegistrationSignature
msgHash := params.PubkeyRegistrationMessageHash
// a message parameter to validate that this signature is intended solely for RegisterBLSPublicKey.
// The message should contain (ExoCore-$chain-id-$operator-address) marker to prevent replay attacks.
// Looking forward to this format: "ExoCore-exocorelocalnet_232-exo13h6xg79g82e2g2vhjwg7j4r2z2hlncelwutkjr"
// Note that the address of the operator must be lowercase
expectedMessage := "ExoCore-" + types.ChainIDWithoutRevision(ctx.ChainID()) + "-" + strings.ToLower(params.OperatorAddress.String())

if params.Message != expectedMessage {
return fmt.Errorf("invalid message format: %s", params.Message)
}
// check msg hash
msgHash := params.PubKeyRegistrationMessageHash

expectedHash := crypto.Keccak256Hash([]byte(expectedMessage))
if !bytes.Equal(msgHash, expectedHash.Bytes()) {
return fmt.Errorf("the input hash failed to validate: %s", params.Message)
}

sig := params.PubKeyRegistrationSignature
pubKey, _ := bls.PublicKeyFromBytes(params.PubKey)
valid, err := blst.VerifySignature(sig, [32]byte(msgHash), pubKey)
if err != nil || !valid {
Expand All @@ -298,18 +314,18 @@ func (k Keeper) RegisterBLSPublicKey(ctx sdk.Context, params *types.BlsParams) e
if k.IsExistPubKeyForAVS(ctx, params.OperatorAddress.String(), params.AvsAddress.String()) {
return errorsmod.Wrap(types.ErrAlreadyExists, fmt.Sprintf("the operator is :%s", params.OperatorAddress))
}
bls := &types.BlsPubKeyInfo{
blsInfo := &types.BlsPubKeyInfo{
AvsAddress: strings.ToLower(params.AvsAddress.String()),
OperatorAddress: strings.ToLower(params.OperatorAddress.String()),
PubKey: params.PubKey,
}
// check a bls key can only be used once.
// if operator are using multiple servers for different AVSs .
// In case one server is compromised, signing can continue as expected on the AVSs for which there has been no compromise.
if k.IsExistPubKey(ctx, bls) {
return errorsmod.Wrap(types.ErrAlreadyExists, fmt.Sprintf("the bls key is already exists:%s", bls.PubKey))
if k.IsExistPubKey(ctx, blsInfo) {
return errorsmod.Wrap(types.ErrAlreadyExists, fmt.Sprintf("the bls key is already exists:%s", blsInfo.PubKey))
}
return k.SetOperatorPubKey(ctx, bls)
return k.SetOperatorPubKey(ctx, blsInfo)
}

func (k Keeper) OperatorOptAction(ctx sdk.Context, params *types.OperatorOptParams) error {
Expand Down
5 changes: 3 additions & 2 deletions x/avs/types/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ type BlsParams struct {
OperatorAddress sdk.AccAddress
AvsAddress common.Address
PubKey []byte
PubkeyRegistrationSignature []byte
PubkeyRegistrationMessageHash []byte
PubKeyRegistrationSignature []byte
PubKeyRegistrationMessageHash []byte
Message string
}

type ProofParams struct {
Expand Down
Loading