Skip to content

Commit

Permalink
fix(avs): prevent BLS12-381 replay attacks (#314)
Browse files Browse the repository at this point in the history
* refactor: improve bls signature validation logic

* test: add unit tests for BLS signature functionality

* rename the bls variable name

* update the pre-compiled bls key registration interface parameters

* remove redundant parameters

* update comments

* fix some comments

* refactor(avs): make bls-msg fmt + update tests

* test(avs): remove redundant return

* test(avs): update x/avs tests

* fix(avs): check bls key format

* test(avs): fix test type

* fix comments

---------

Co-authored-by: MaxMustermann2 <[email protected]>
  • Loading branch information
trestinlsd and MaxMustermann2 authored Feb 24, 2025
1 parent a4d1dae commit 505516b
Show file tree
Hide file tree
Showing 8 changed files with 281 additions and 49 deletions.
6 changes: 2 additions & 4 deletions precompiles/avs/IAVSManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,12 @@ 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 bls signature of the operator
function registerBLSPublicKey(
address sender,
address avsAddress,
bytes calldata pubKey,
bytes calldata pubkeyRegistrationSignature,
bytes calldata pubkeyRegistrationMessageHash
bytes calldata pubKeyRegistrationSignature
) external returns (bool success);

/// @dev operatorSubmitTask , this function enables a operator submit a task result.
Expand Down
7 changes: 1 addition & 6 deletions precompiles/avs/abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1021,12 +1021,7 @@
},
{
"internalType": "bytes",
"name": "pubkeyRegistrationSignature",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "pubkeyRegistrationMessageHash",
"name": "pubKeyRegistrationSignature",
"type": "bytes"
}
],
Expand Down
3 changes: 1 addition & 2 deletions precompiles/avs/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@ var baseTestCases = []avsTestCases{

func (suite *AVSManagerPrecompileSuite) TestGetOptedInOperatorAccAddrs() {
method := suite.precompile.Methods[avsManagerPrecompile.MethodGetOptInOperators]
operatorAddress, avsAddress, slashContract :=
sdk.AccAddress(utiltx.GenerateAddress().Bytes()).String(), suite.Address, "0xDF907c29719154eb9872f021d21CAE6E5025d7aB"
operatorAddress, avsAddress, slashContract := sdk.AccAddress(utiltx.GenerateAddress().Bytes()).String(), suite.Address, "0xDF907c29719154eb9872f021d21CAE6E5025d7aB"

operatorOptIn := func() {
optedInfo := &types.OptedInfo{
Expand Down
10 changes: 2 additions & 8 deletions precompiles/avs/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,14 +314,8 @@ func (p Precompile) RegisterBLSPublicKey(
if !ok {
return nil, fmt.Errorf(imuacmn.ErrContractInputParamOrType, 3, "[]byte", pubKeyRegistrationSignature)
}
blsParams.PubkeyRegistrationSignature = pubKeyRegistrationSignature

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

blsParams.PubKeyRegistrationSignature = pubKeyRegistrationSignature
// validates key format by itself so we don't need to do it before this.
err := p.avsKeeper.RegisterBLSPublicKey(ctx, blsParams)
if err != nil {
return nil, err
Expand Down
242 changes: 234 additions & 8 deletions x/avs/keeper/avs_test.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
package keeper_test

import (
"fmt"
"math/big"
"strings"
"time"

"github.com/ethereum/go-ethereum/crypto"
testutiltx "github.com/imua-xyz/imuachain/testutil/tx"
"github.com/prysmaticlabs/prysm/v4/crypto/bls/blst"

errorsmod "cosmossdk.io/errors"
"cosmossdk.io/math"
"github.com/ethereum/go-ethereum/common"
assetstypes "github.com/imua-xyz/imuachain/x/assets/types"

sdk "github.com/cosmos/cosmos-sdk/types"
utiltx "github.com/evmos/evmos/v16/testutil/tx"
"github.com/imua-xyz/imuachain/x/avs/types"
avstypes "github.com/imua-xyz/imuachain/x/avs/types"
delegationtypes "github.com/imua-xyz/imuachain/x/delegation/types"
epochstypes "github.com/imua-xyz/imuachain/x/epochs/types"
operatorTypes "github.com/imua-xyz/imuachain/x/operator/types"

sdk "github.com/cosmos/cosmos-sdk/types"
utiltx "github.com/evmos/evmos/v16/testutil/tx"
avstypes "github.com/imua-xyz/imuachain/x/avs/types"
)

func (suite *AVSTestSuite) TestAVS() {
Expand Down Expand Up @@ -86,7 +93,7 @@ func (suite *AVSTestSuite) TestUpdateAVSInfo_Register() {
avsParams := &types.AVSRegisterOrDeregisterParams{
AvsName: avsName,
AvsAddress: common.HexToAddress(avsAddres),
Action: avstypes.RegisterAction,
Action: types.RegisterAction,
RewardContractAddress: common.HexToAddress(rewardAddress),
AvsOwnerAddresses: avsOwnerAddress,
AssetIDs: assetIDs,
Expand Down Expand Up @@ -122,7 +129,7 @@ func (suite *AVSTestSuite) TestUpdateAVSInfo_DeRegister() {
avsParams := &types.AVSRegisterOrDeregisterParams{
AvsName: avsName,
AvsAddress: common.HexToAddress(avsAddress),
Action: avstypes.DeRegisterAction,
Action: types.DeRegisterAction,
AvsOwnerAddresses: avsOwnerAddress,
AssetIDs: assetIDs,
MinSelfDelegation: uint64(10),
Expand All @@ -135,7 +142,7 @@ func (suite *AVSTestSuite) TestUpdateAVSInfo_DeRegister() {
suite.Error(err)
suite.Contains(err.Error(), types.ErrUnregisterNonExistent.Error())

avsParams.Action = avstypes.RegisterAction
avsParams.Action = types.RegisterAction
err = suite.App.AVSManagerKeeper.UpdateAVSInfo(suite.Ctx, avsParams)
suite.NoError(err)
info, err := suite.App.AVSManagerKeeper.GetAVSInfo(suite.Ctx, avsAddress)
Expand All @@ -153,21 +160,23 @@ func (suite *AVSTestSuite) TestUpdateAVSInfo_DeRegister() {

avsParams.Action = avstypes.DeRegisterAction
avsParams.CallerAddress, err = sdk.AccAddressFromBech32(avsOwnerAddress[0])

suite.NoError(err)
err = suite.App.AVSManagerKeeper.UpdateAVSInfo(suite.Ctx, avsParams)
suite.NoError(err)
info, err = suite.App.AVSManagerKeeper.GetAVSInfo(suite.Ctx, avsAddress)
suite.Error(err)
suite.Contains(err.Error(), types.ErrNoKeyInTheStore.Error())
suite.Nil(info)
}

func (suite *AVSTestSuite) TestUpdateAVSInfoWithOperator_Register() {
avsAddress := suite.avsAddress
operatorAddress := sdk.AccAddress(utiltx.GenerateAddress().Bytes())

operatorParams := &avstypes.OperatorOptParams{
operatorParams := &types.OperatorOptParams{
AvsAddress: avsAddress,
Action: avstypes.RegisterAction,
Action: types.RegisterAction,
OperatorAddress: operatorAddress,
}
// operator Not Exist
Expand Down Expand Up @@ -209,3 +218,220 @@ func (suite *AVSTestSuite) TestAddressSwitch() {
commonAddress := common.Address(accAddress)
suite.Equal(common.HexToAddress("0x8dF46478a83Ab2a429979391E9546A12AfF9E33f"), commonAddress)
}

func (suite *AVSTestSuite) TestRegisterBLSPublicKey() {
type testCase struct {
name string
setupParams func() *types.BlsParams
errorContains string
}

testCases := []testCase{
{
name: "successful registration",
setupParams: func() *types.BlsParams {
privateKey, err := blst.RandKey()
suite.NoError(err)
operatorAddress := sdk.AccAddress(utiltx.GenerateAddress().Bytes())
msg := fmt.Sprintf(types.BLSMessageToSign, types.ChainIDWithoutRevision(suite.Ctx.ChainID()), operatorAddress.String())
hashedMsg := crypto.Keccak256Hash([]byte(msg))
sig := privateKey.Sign(hashedMsg.Bytes())

return &types.BlsParams{
OperatorAddress: operatorAddress,
AvsAddress: testutiltx.GenerateAddress(),
PubKey: privateKey.PublicKey().Marshal(),
PubKeyRegistrationSignature: sig.Marshal(),
}
},
},
{
name: "reuse BLS key - same operator + avs",
setupParams: func() *types.BlsParams {
privateKey, err := blst.RandKey()
suite.NoError(err)
operatorAddress := sdk.AccAddress(utiltx.GenerateAddress().Bytes())
msg := fmt.Sprintf(types.BLSMessageToSign, types.ChainIDWithoutRevision(suite.Ctx.ChainID()), operatorAddress.String())
hashedMsg := crypto.Keccak256Hash([]byte(msg))
sig := privateKey.Sign(hashedMsg.Bytes())

params := types.BlsParams{
OperatorAddress: operatorAddress,
AvsAddress: testutiltx.GenerateAddress(),
PubKey: privateKey.PublicKey().Marshal(),
PubKeyRegistrationSignature: sig.Marshal(),
}
err = suite.App.AVSManagerKeeper.RegisterBLSPublicKey(suite.Ctx, &params)
suite.NoError(err)
return &params
},
errorContains: errorsmod.Wrap(
types.ErrAlreadyExists,
"a key has already been set for this operator and avs",
).Error(),
},
{
name: "reuse BLS key - different operator + same avs",
setupParams: func() *types.BlsParams {
privateKey, err := blst.RandKey()
suite.NoError(err)
operatorAddress := sdk.AccAddress(utiltx.GenerateAddress().Bytes())
msg := fmt.Sprintf(types.BLSMessageToSign, types.ChainIDWithoutRevision(suite.Ctx.ChainID()), operatorAddress.String())
hashedMsg := crypto.Keccak256Hash([]byte(msg))
sig := privateKey.Sign(hashedMsg.Bytes())

params := types.BlsParams{
OperatorAddress: operatorAddress,
AvsAddress: testutiltx.GenerateAddress(),
PubKey: privateKey.PublicKey().Marshal(),
PubKeyRegistrationSignature: sig.Marshal(),
}
err = suite.App.AVSManagerKeeper.RegisterBLSPublicKey(suite.Ctx, &params)
suite.NoError(err)

anotherOperatorAddress := sdk.AccAddress(utiltx.GenerateAddress().Bytes())
msg = fmt.Sprintf(types.BLSMessageToSign, types.ChainIDWithoutRevision(suite.Ctx.ChainID()), anotherOperatorAddress.String())
hashedMsg = crypto.Keccak256Hash([]byte(msg))
sig = privateKey.Sign(hashedMsg.Bytes())

return &types.BlsParams{
OperatorAddress: anotherOperatorAddress,
AvsAddress: params.AvsAddress,
PubKey: privateKey.PublicKey().Marshal(),
PubKeyRegistrationSignature: sig.Marshal(),
}
},
errorContains: errorsmod.Wrap(
types.ErrAlreadyExists,
"this BLS key is already in use",
).Error(),
},
{
name: "wrong chain ID",
setupParams: func() *types.BlsParams {
privateKey, err := blst.RandKey()
suite.NoError(err)
operatorAddress := sdk.AccAddress(utiltx.GenerateAddress().Bytes())
msg := fmt.Sprintf(types.BLSMessageToSign, "onemorechain_211", operatorAddress.String())
hashedMsg := crypto.Keccak256Hash([]byte(msg))
sig := privateKey.Sign(hashedMsg.Bytes())

return &types.BlsParams{
OperatorAddress: operatorAddress,
AvsAddress: testutiltx.GenerateAddress(),
PubKey: privateKey.PublicKey().Marshal(),
PubKeyRegistrationSignature: sig.Marshal(),
}
},
errorContains: types.ErrSigNotMatchPubKey.Error(),
},
{
name: "wrong operator address in signature",
setupParams: func() *types.BlsParams {
privateKey, err := blst.RandKey()
suite.NoError(err)
operatorAddress := sdk.AccAddress(utiltx.GenerateAddress().Bytes())
anotherOperatorAddress := sdk.AccAddress(utiltx.GenerateAddress().Bytes())
msg := fmt.Sprintf(types.BLSMessageToSign, types.ChainIDWithoutRevision(suite.Ctx.ChainID()), anotherOperatorAddress.String())
hashedMsg := crypto.Keccak256Hash([]byte(msg))
sig := privateKey.Sign(hashedMsg.Bytes())

return &types.BlsParams{
OperatorAddress: operatorAddress,
AvsAddress: testutiltx.GenerateAddress(),
PubKey: privateKey.PublicKey().Marshal(),
PubKeyRegistrationSignature: sig.Marshal(),
}
},
errorContains: types.ErrSigNotMatchPubKey.Error(),
},
{
name: "mismatched BLS key",
setupParams: func() *types.BlsParams {
privateKey, err := blst.RandKey()
suite.NoError(err)
operatorAddress := sdk.AccAddress(utiltx.GenerateAddress().Bytes())
msg := fmt.Sprintf(types.BLSMessageToSign, types.ChainIDWithoutRevision(suite.Ctx.ChainID()), operatorAddress.String())
hashedMsg := crypto.Keccak256Hash([]byte(msg))
sig := privateKey.Sign(hashedMsg.Bytes())
// generate a different private key
anotherPrivateKey, err := blst.RandKey()
suite.NoError(err)
anotherPublicKey := anotherPrivateKey.PublicKey()
return &types.BlsParams{
OperatorAddress: operatorAddress,
AvsAddress: testutiltx.GenerateAddress(),
// provide a different public key than the one which signed it, so that verification fails
PubKey: anotherPublicKey.Marshal(),
PubKeyRegistrationSignature: sig.Marshal(),
}
},
errorContains: types.ErrSigNotMatchPubKey.Error(),
},
{
name: "invalid public key format",
setupParams: func() *types.BlsParams {
privateKey, err := blst.RandKey()
suite.NoError(err)
operatorAddress := sdk.AccAddress(utiltx.GenerateAddress().Bytes())
msg := fmt.Sprintf(types.BLSMessageToSign, types.ChainIDWithoutRevision(suite.Ctx.ChainID()), operatorAddress.String())
hashedMsg := crypto.Keccak256Hash([]byte(msg))
sig := privateKey.Sign(hashedMsg.Bytes())
return &types.BlsParams{
OperatorAddress: operatorAddress,
AvsAddress: testutiltx.GenerateAddress(),
PubKey: []byte("invalid"),
PubKeyRegistrationSignature: sig.Marshal(),
}
},
errorContains: types.ErrParsePubKey.Error(),
},
{
name: "invalid signature format",
setupParams: func() *types.BlsParams {
privateKey, err := blst.RandKey()
suite.NoError(err)
operatorAddress := sdk.AccAddress(utiltx.GenerateAddress().Bytes())
return &types.BlsParams{
OperatorAddress: operatorAddress,
AvsAddress: testutiltx.GenerateAddress(),
PubKey: privateKey.PublicKey().Marshal(),
PubKeyRegistrationSignature: []byte("invalid"),
}
},
errorContains: types.ErrSigNotMatchPubKey.Error(),
},
{
name: "empty operator address",
setupParams: func() *types.BlsParams {
operatorAddress := sdk.AccAddress{}
privateKey, err := blst.RandKey()
suite.NoError(err)
msg := fmt.Sprintf(types.BLSMessageToSign, types.ChainIDWithoutRevision(suite.Ctx.ChainID()), operatorAddress.String())
hashedMsg := crypto.Keccak256Hash([]byte(msg))
sig := privateKey.Sign(hashedMsg.Bytes())
return &types.BlsParams{
OperatorAddress: operatorAddress,
AvsAddress: testutiltx.GenerateAddress(),
PubKey: privateKey.PublicKey().Marshal(),
PubKeyRegistrationSignature: sig.Marshal(),
}
},
errorContains: types.ErrInvalidAddr.Error(),
},
}

for _, tc := range testCases {
suite.Run(tc.name, func() {
params := tc.setupParams()
err := suite.App.AVSManagerKeeper.RegisterBLSPublicKey(suite.Ctx, params)

if tc.errorContains != "" {
suite.Error(err)
suite.Contains(err.Error(), tc.errorContains)
} else {
suite.NoError(err)
}
})
}
}
Loading

0 comments on commit 505516b

Please sign in to comment.