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

[v2] Blob Header Authentication #836

Merged
merged 2 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions core/auth.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
package core

import (
"bytes"
"errors"
"fmt"

geth "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)

type BlobRequestAuthenticator interface {
AuthenticateBlobRequest(header BlobAuthHeader) error
}
Expand All @@ -8,3 +18,34 @@ type BlobRequestSigner interface {
SignBlobRequest(header BlobAuthHeader) ([]byte, error)
GetAccountID() (string, error)
}

func VerifySignature(message []byte, accountAddr geth.Address, sig []byte) error {
// Ensure the signature is 65 bytes (Recovery ID is the last byte)
if len(sig) != 65 {
return fmt.Errorf("signature length is unexpected: %d", len(sig))
}

publicKeyBytes, err := hexutil.Decode(accountAddr.Hex())
if err != nil {
return fmt.Errorf("failed to decode public key (%v): %v", accountAddr.Hex(), err)
}

// Decode public key
pubKey, err := crypto.UnmarshalPubkey(publicKeyBytes)
if err != nil {
return fmt.Errorf("failed to decode public key (%v): %v", accountAddr.Hex(), err)
}

// Verify the signature
sigPublicKeyECDSA, err := crypto.SigToPub(message, sig)
if err != nil {
return fmt.Errorf("failed to recover public key from signature: %v", err)
}

if !bytes.Equal(pubKey.X.Bytes(), sigPublicKeyECDSA.X.Bytes()) || !bytes.Equal(pubKey.Y.Bytes(), sigPublicKeyECDSA.Y.Bytes()) {
return errors.New("signature doesn't match with provided public key")
}

return nil

}
4 changes: 2 additions & 2 deletions core/auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
func TestAuthentication(t *testing.T) {

// Make the authenticator
authenticator := auth.NewAuthenticator(auth.AuthConfig{})
authenticator := auth.NewAuthenticator()

// Make the signer
privateKeyHex := "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
Expand Down Expand Up @@ -43,7 +43,7 @@ func TestAuthentication(t *testing.T) {
func TestAuthenticationFail(t *testing.T) {

// Make the authenticator
authenticator := auth.NewAuthenticator(auth.AuthConfig{})
authenticator := auth.NewAuthenticator()

// Make the signer
privateKeyHex := "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
Expand Down
14 changes: 4 additions & 10 deletions core/auth/authenticator.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,12 @@ import (
"github.com/ethereum/go-ethereum/crypto"
)

type AuthConfig struct {
}

type authenticator struct {
config AuthConfig
}
type authenticator struct{}

func NewAuthenticator(config AuthConfig) core.BlobRequestAuthenticator {
var _ core.BlobRequestAuthenticator = &authenticator{}

return &authenticator{
config: config,
}
func NewAuthenticator() core.BlobRequestAuthenticator {
return &authenticator{}
}

func (*authenticator) AuthenticateBlobRequest(header core.BlobAuthHeader) error {
Expand Down
2 changes: 2 additions & 0 deletions core/auth/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ func (s *LocalBlobRequestSigner) GetAccountID() (string, error) {

type LocalNoopSigner struct{}

var _ core.BlobRequestSigner = &LocalNoopSigner{}

func NewLocalNoopSigner() *LocalNoopSigner {
return &LocalNoopSigner{}
}
Expand Down
116 changes: 116 additions & 0 deletions core/auth/v2/auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package v2_test

import (
"math/big"
"testing"

"github.com/Layr-Labs/eigenda/core"
auth "github.com/Layr-Labs/eigenda/core/auth/v2"
corev2 "github.com/Layr-Labs/eigenda/core/v2"
"github.com/Layr-Labs/eigenda/encoding"
"github.com/consensys/gnark-crypto/ecc/bn254/fp"
"github.com/stretchr/testify/assert"
)

var (
privateKeyHex = "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
)

func TestAuthentication(t *testing.T) {
signer := auth.NewLocalBlobRequestSigner(privateKeyHex)
authenticator := auth.NewAuthenticator()

accountId, err := signer.GetAccountID()
assert.NoError(t, err)
header := testHeader(t, accountId)

// Sign the header
signature, err := signer.SignBlobRequest(header)
assert.NoError(t, err)

header.Signature = signature

err = authenticator.AuthenticateBlobRequest(header)
assert.NoError(t, err)

}

func TestAuthenticationFail(t *testing.T) {
signer := auth.NewLocalBlobRequestSigner(privateKeyHex)
authenticator := auth.NewAuthenticator()

accountId, err := signer.GetAccountID()
assert.NoError(t, err)

header := testHeader(t, accountId)

wrongPrivateKeyHex := "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcded"
signer = auth.NewLocalBlobRequestSigner(wrongPrivateKeyHex)

// Sign the header
signature, err := signer.SignBlobRequest(header)
assert.NoError(t, err)

header.Signature = signature

err = authenticator.AuthenticateBlobRequest(header)
assert.Error(t, err)
}

func TestNoopSignerFail(t *testing.T) {
signer := auth.NewLocalNoopSigner()
accountId, err := signer.GetAccountID()
assert.EqualError(t, err, "noop signer cannot get accountID")

header := testHeader(t, accountId)

_, err = signer.SignBlobRequest(header)
assert.EqualError(t, err, "noop signer cannot sign blob request")
}

func testHeader(t *testing.T, accountID string) *corev2.BlobHeader {
var commitX, commitY fp.Element
_, err := commitX.SetString("21661178944771197726808973281966770251114553549453983978976194544185382599016")
assert.NoError(t, err)
_, err = commitY.SetString("9207254729396071334325696286939045899948985698134704137261649190717970615186")
assert.NoError(t, err)

commitment := &encoding.G1Commitment{
X: commitX,
Y: commitY,
}
var lengthXA0, lengthXA1, lengthYA0, lengthYA1 fp.Element
_, err = lengthXA0.SetString("10857046999023057135944570762232829481370756359578518086990519993285655852781")
assert.NoError(t, err)
_, err = lengthXA1.SetString("11559732032986387107991004021392285783925812861821192530917403151452391805634")
assert.NoError(t, err)
_, err = lengthYA0.SetString("8495653923123431417604973247489272438418190587263600148770280649306958101930")
assert.NoError(t, err)
_, err = lengthYA1.SetString("4082367875863433681332203403145435568316851327593401208105741076214120093531")
assert.NoError(t, err)

var lengthProof, lengthCommitment encoding.G2Commitment
lengthProof.X.A0 = lengthXA0
lengthProof.X.A1 = lengthXA1
lengthProof.Y.A0 = lengthYA0
lengthProof.Y.A1 = lengthYA1

lengthCommitment = lengthProof

return &corev2.BlobHeader{
BlobVersion: 0,
BlobCommitments: encoding.BlobCommitments{
Commitment: commitment,
LengthCommitment: &lengthCommitment,
LengthProof: &lengthProof,
Length: 50,
},
QuorumNumbers: []core.QuorumID{0, 1},
PaymentMetadata: core.PaymentMetadata{
AccountID: accountID,
BinIndex: 5,
CumulativePayment: big.NewInt(100),
},
Signature: []byte{},
}
}
57 changes: 57 additions & 0 deletions core/auth/v2/authenticator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package v2

import (
"bytes"
"errors"
"fmt"

core "github.com/Layr-Labs/eigenda/core/v2"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)

type authenticator struct{}

func NewAuthenticator() *authenticator {
return &authenticator{}
}

var _ core.BlobRequestAuthenticator = &authenticator{}

func (*authenticator) AuthenticateBlobRequest(header *core.BlobHeader) error {
sig := header.Signature

// Ensure the signature is 65 bytes (Recovery ID is the last byte)
if len(sig) != 65 {
return fmt.Errorf("signature length is unexpected: %d", len(sig))
}

blobKey, err := header.BlobKey()
if err != nil {
return fmt.Errorf("failed to get blob key: %v", err)
}

publicKeyBytes, err := hexutil.Decode(header.PaymentMetadata.AccountID)
if err != nil {
return fmt.Errorf("failed to decode public key (%v): %v", header.PaymentMetadata.AccountID, err)
}

// Decode public key
pubKey, err := crypto.UnmarshalPubkey(publicKeyBytes)
if err != nil {
return fmt.Errorf("failed to decode public key (%v): %v", header.PaymentMetadata.AccountID, err)
}

// Verify the signature
sigPublicKeyECDSA, err := crypto.SigToPub(blobKey[:], sig)
if err != nil {
return fmt.Errorf("failed to recover public key from signature: %v", err)
}

if !bytes.Equal(pubKey.X.Bytes(), sigPublicKeyECDSA.X.Bytes()) || !bytes.Equal(pubKey.Y.Bytes(), sigPublicKeyECDSA.Y.Bytes()) {
return errors.New("signature doesn't match with provided public key")
}

return nil
}
68 changes: 68 additions & 0 deletions core/auth/v2/signer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package v2

import (
"crypto/ecdsa"
"fmt"
"log"

core "github.com/Layr-Labs/eigenda/core/v2"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)

type LocalBlobRequestSigner struct {
PrivateKey *ecdsa.PrivateKey
}

var _ core.BlobRequestSigner = &LocalBlobRequestSigner{}

func NewLocalBlobRequestSigner(privateKeyHex string) *LocalBlobRequestSigner {
privateKeyBytes := common.FromHex(privateKeyHex)
privateKey, err := crypto.ToECDSA(privateKeyBytes)
if err != nil {
log.Fatalf("Failed to parse private key: %v", err)
}

return &LocalBlobRequestSigner{
PrivateKey: privateKey,
}
}

func (s *LocalBlobRequestSigner) SignBlobRequest(header *core.BlobHeader) ([]byte, error) {
blobKey, err := header.BlobKey()
if err != nil {
return nil, fmt.Errorf("failed to get blob key: %v", err)
}

// Sign the blob key using the private key
sig, err := crypto.Sign(blobKey[:], s.PrivateKey)
if err != nil {
return nil, fmt.Errorf("failed to sign hash: %v", err)
}

return sig, nil
}

func (s *LocalBlobRequestSigner) GetAccountID() (string, error) {

publicKeyBytes := crypto.FromECDSAPub(&s.PrivateKey.PublicKey)
return hexutil.Encode(publicKeyBytes), nil

}

type LocalNoopSigner struct{}

var _ core.BlobRequestSigner = &LocalNoopSigner{}

func NewLocalNoopSigner() *LocalNoopSigner {
return &LocalNoopSigner{}
}

func (s *LocalNoopSigner) SignBlobRequest(header *core.BlobHeader) ([]byte, error) {
return nil, fmt.Errorf("noop signer cannot sign blob request")
}

func (s *LocalNoopSigner) GetAccountID() (string, error) {
return "", fmt.Errorf("noop signer cannot get accountID")
}
10 changes: 10 additions & 0 deletions core/v2/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package v2

type BlobRequestAuthenticator interface {
AuthenticateBlobRequest(header *BlobHeader) error
}

type BlobRequestSigner interface {
SignBlobRequest(header *BlobHeader) ([]byte, error)
GetAccountID() (string, error)
}
2 changes: 1 addition & 1 deletion disperser/apiserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func NewDispersalServer(
}
logger.Info("allowlist config", "file", rateConfig.AllowlistFile, "refreshInterval", rateConfig.AllowlistRefreshInterval.String())

authenticator := auth.NewAuthenticator(auth.AuthConfig{})
authenticator := auth.NewAuthenticator()

return &DispersalServer{
serverConfig: serverConfig,
Expand Down
Loading