Skip to content

Commit ba0a958

Browse files
author
yujiezhu0
committed
Separate constructors for NewBlobRequestAuthenticator and NewPaymentStateAuthenticator
1 parent 17f0cbb commit ba0a958

File tree

11 files changed

+102
-65
lines changed

11 files changed

+102
-65
lines changed

api/clients/v2/disperser_client.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,15 +296,17 @@ func (c *disperserClient) GetPaymentState(ctx context.Context) (*disperser_rpc.G
296296
return nil, fmt.Errorf("error getting signer's account ID: %w", err)
297297
}
298298

299-
signature, err := c.signer.SignPaymentStateRequest()
299+
timestamp := uint64(time.Now().UnixNano())
300+
301+
signature, err := c.signer.SignPaymentStateRequest(timestamp)
300302
if err != nil {
301303
return nil, fmt.Errorf("error signing payment state request: %w", err)
302304
}
303305

304306
request := &disperser_rpc.GetPaymentStateRequest{
305307
AccountId: accountID.Hex(),
306308
Signature: signature,
307-
Timestamp: uint64(time.Now().UnixNano()),
309+
Timestamp: timestamp,
308310
}
309311
return c.client.GetPaymentState(ctx, request)
310312
}

api/hashing/payment_state_hashing.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,30 @@ package hashing
33
import (
44
"fmt"
55
pb "github.com/Layr-Labs/eigenda/api/grpc/disperser/v2"
6+
"github.com/ethereum/go-ethereum/common"
67
"golang.org/x/crypto/sha3"
78
)
89

9-
// HashGetPaymentStateRequest hashes the given GetPaymentStateRequest.
10-
func HashGetPaymentStateRequest(request *pb.GetPaymentStateRequest) ([]byte, error) {
10+
// HashGetPaymentStateRequestFromFields hashes the given GetPaymentStateRequest from accountId and timestamp
11+
func HashGetPaymentStateRequestFromFields(accountId common.Address, timestamp uint64) ([]byte, error) {
1112
hasher := sha3.NewLegacyKeccak256()
1213

13-
// Hash the account ID
14-
err := hashByteArray(hasher, []byte(request.GetAccountId()))
14+
// Hash the accountId
15+
err := hashByteArray(hasher, accountId.Bytes())
1516
if err != nil {
1617
return nil, fmt.Errorf("failed to hash account id: %w", err)
1718
}
1819

1920
// Hash the timestamp
20-
hashUint64(hasher, request.GetTimestamp())
21+
hashUint64(hasher, timestamp)
2122

2223
return hasher.Sum(nil), nil
2324
}
25+
26+
// HashGetPaymentStateRequestFromRequest hashes the given GetPaymentStateRequest from request
27+
func HashGetPaymentStateRequestFromRequest(request *pb.GetPaymentStateRequest) ([]byte, error) {
28+
accountId := common.HexToAddress(request.GetAccountId())
29+
timestamp := request.GetTimestamp()
30+
31+
return HashGetPaymentStateRequestFromFields(accountId, timestamp)
32+
}

core/auth/v2/auth_test.go

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package v2_test
33
import (
44
"crypto/sha256"
55
disperser_rpc "github.com/Layr-Labs/eigenda/api/grpc/disperser/v2"
6+
"github.com/Layr-Labs/eigenda/common/replay"
67
"math/big"
78
"testing"
89
"time"
@@ -18,15 +19,16 @@ import (
1819
)
1920

2021
var (
21-
privateKeyHex = "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
22-
maxPastAge = 5 * time.Minute
23-
maxFutureAge = 5 * time.Minute
22+
privateKeyHex = "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
23+
maxPastAge = 5 * time.Minute
24+
maxFutureAge = 5 * time.Minute
25+
fixedTimestamp = uint64(1609459200000000000)
2426
)
2527

2628
func TestAuthentication(t *testing.T) {
2729
signer, err := auth.NewLocalBlobRequestSigner(privateKeyHex)
2830
assert.NoError(t, err)
29-
authenticator := auth.NewAuthenticator(maxPastAge, maxFutureAge)
31+
blobRequestAuthenticator := auth.NewBlobRequestAuthenticator()
3032

3133
accountId, err := signer.GetAccountID()
3234
assert.NoError(t, err)
@@ -36,14 +38,14 @@ func TestAuthentication(t *testing.T) {
3638
signature, err := signer.SignBlobRequest(header)
3739
assert.NoError(t, err)
3840

39-
err = authenticator.AuthenticateBlobRequest(header, signature)
41+
err = blobRequestAuthenticator.AuthenticateBlobRequest(header, signature)
4042
assert.NoError(t, err)
4143
}
4244

4345
func TestAuthenticationFail(t *testing.T) {
4446
signer, err := auth.NewLocalBlobRequestSigner(privateKeyHex)
4547
assert.NoError(t, err)
46-
authenticator := auth.NewAuthenticator(maxPastAge, maxFutureAge)
48+
blobRequestAuthenticator := auth.NewBlobRequestAuthenticator()
4749

4850
accountId, err := signer.GetAccountID()
4951
assert.NoError(t, err)
@@ -58,7 +60,7 @@ func TestAuthenticationFail(t *testing.T) {
5860
signature, err := signer.SignBlobRequest(header)
5961
assert.NoError(t, err)
6062

61-
err = authenticator.AuthenticateBlobRequest(header, signature)
63+
err = blobRequestAuthenticator.AuthenticateBlobRequest(header, signature)
6264
assert.Error(t, err)
6365
}
6466

@@ -122,42 +124,43 @@ func testHeader(t *testing.T, accountID gethcommon.Address) *corev2.BlobHeader {
122124
func TestAuthenticatePaymentStateRequestValid(t *testing.T) {
123125
signer, err := auth.NewLocalBlobRequestSigner(privateKeyHex)
124126
assert.NoError(t, err)
125-
authenticator := auth.NewAuthenticator(maxPastAge, maxFutureAge)
127+
paymentStateAuthenticator := auth.NewPaymentStateAuthenticator(maxPastAge, maxFutureAge)
128+
paymentStateAuthenticator.ReplayGuardian = replay.NewNoOpReplayGuardian()
126129

127-
signature, err := signer.SignPaymentStateRequest()
130+
signature, err := signer.SignPaymentStateRequest(fixedTimestamp)
128131
assert.NoError(t, err)
132+
assert.NotNil(t, signature)
129133

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

133137
request := mockGetPaymentStateRequest(accountId, signature)
134138

135-
err = authenticator.AuthenticatePaymentStateRequest(accountId, request)
139+
err = paymentStateAuthenticator.AuthenticatePaymentStateRequest(accountId, request)
136140
assert.NoError(t, err)
137141
}
138142

139143
func TestAuthenticatePaymentStateRequestInvalidSignatureLength(t *testing.T) {
140-
authenticator := auth.NewAuthenticator(maxPastAge, maxFutureAge)
144+
paymentStateAuthenticator := auth.NewPaymentStateAuthenticator(maxPastAge, maxFutureAge)
141145
request := mockGetPaymentStateRequest(gethcommon.HexToAddress("0x123"), []byte{1, 2, 3})
142146

143-
err := authenticator.AuthenticatePaymentStateRequest(gethcommon.HexToAddress("0x123"), request)
147+
err := paymentStateAuthenticator.AuthenticatePaymentStateRequest(gethcommon.HexToAddress("0x123"), request)
144148
assert.Error(t, err)
145149
assert.Contains(t, err.Error(), "signature length is unexpected")
146150
}
147151

148152
func TestAuthenticatePaymentStateRequestInvalidPublicKey(t *testing.T) {
149-
authenticator := auth.NewAuthenticator(maxPastAge, maxFutureAge)
153+
paymentStateAuthenticator := auth.NewPaymentStateAuthenticator(maxPastAge, maxFutureAge)
150154
request := mockGetPaymentStateRequest(gethcommon.Address{}, make([]byte, 65))
151-
152-
err := authenticator.AuthenticatePaymentStateRequest(gethcommon.Address{}, request)
155+
err := paymentStateAuthenticator.AuthenticatePaymentStateRequest(gethcommon.Address{}, request)
153156
assert.Error(t, err)
154157
assert.Contains(t, err.Error(), "failed to recover public key from signature")
155158
}
156159

157160
func TestAuthenticatePaymentStateRequestSignatureMismatch(t *testing.T) {
158161
signer, err := auth.NewLocalBlobRequestSigner(privateKeyHex)
159162
assert.NoError(t, err)
160-
authenticator := auth.NewAuthenticator(maxPastAge, maxFutureAge)
163+
paymentStateAuthenticator := auth.NewPaymentStateAuthenticator(maxPastAge, maxFutureAge)
161164

162165
// Create a different signer with wrong private key
163166
wrongSigner, err := auth.NewLocalBlobRequestSigner("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcded")
@@ -167,20 +170,20 @@ func TestAuthenticatePaymentStateRequestSignatureMismatch(t *testing.T) {
167170
accountId, err := signer.GetAccountID()
168171
assert.NoError(t, err)
169172

170-
signature, err := wrongSigner.SignPaymentStateRequest()
173+
signature, err := wrongSigner.SignPaymentStateRequest(uint64(time.Now().UnixNano()))
171174
assert.NoError(t, err)
172175

173176
request := mockGetPaymentStateRequest(accountId, signature)
174177

175-
err = authenticator.AuthenticatePaymentStateRequest(accountId, request)
178+
err = paymentStateAuthenticator.AuthenticatePaymentStateRequest(accountId, request)
176179
assert.Error(t, err)
177180
assert.Contains(t, err.Error(), "signature doesn't match with provided public key")
178181
}
179182

180183
func TestAuthenticatePaymentStateRequestCorruptedSignature(t *testing.T) {
181184
signer, err := auth.NewLocalBlobRequestSigner(privateKeyHex)
182185
assert.NoError(t, err)
183-
authenticator := auth.NewAuthenticator(maxPastAge, maxFutureAge)
186+
paymentStateAuthenticator := auth.NewPaymentStateAuthenticator(maxPastAge, maxFutureAge)
184187

185188
accountId, err := signer.GetAccountID()
186189
assert.NoError(t, err)
@@ -193,14 +196,14 @@ func TestAuthenticatePaymentStateRequestCorruptedSignature(t *testing.T) {
193196
signature[0] ^= 0x01
194197
request := mockGetPaymentStateRequest(accountId, signature)
195198

196-
err = authenticator.AuthenticatePaymentStateRequest(accountId, request)
199+
err = paymentStateAuthenticator.AuthenticatePaymentStateRequest(accountId, request)
197200
assert.Error(t, err)
198201
}
199202

200203
func mockGetPaymentStateRequest(accountId gethcommon.Address, signature []byte) *disperser_rpc.GetPaymentStateRequest {
201204
return &disperser_rpc.GetPaymentStateRequest{
202205
AccountId: accountId.Hex(),
203206
Signature: signature,
204-
Timestamp: uint64(time.Now().UnixNano()),
207+
Timestamp: fixedTimestamp,
205208
}
206209
}

core/auth/v2/authenticator.go

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,22 @@ import (
1515
)
1616

1717
type authenticator struct {
18-
replayGuardian replay.ReplayGuardian
18+
ReplayGuardian replay.ReplayGuardian
1919
}
2020

21-
func NewAuthenticator(maxTimeInPast, maxTimeInFuture time.Duration) *authenticator {
21+
// NewBlobRequestAuthenticator creates an authenticator for blob requests.
22+
// ReplayGuardian is not used for blob requests.
23+
func NewBlobRequestAuthenticator() *authenticator {
2224
return &authenticator{
23-
replayGuardian: replay.NewReplayGuardian(time.Now, maxTimeInPast, maxTimeInFuture),
25+
ReplayGuardian: nil, // Not needed for blob requests
26+
}
27+
}
28+
29+
// NewPaymentStateAuthenticator creates an authenticator for payment state requests,
30+
// which requires replay protection.
31+
func NewPaymentStateAuthenticator(maxTimeInPast, maxTimeInFuture time.Duration) *authenticator {
32+
return &authenticator{
33+
ReplayGuardian: replay.NewReplayGuardian(time.Now, maxTimeInPast, maxTimeInFuture),
2434
}
2535
}
2636

@@ -54,7 +64,7 @@ func (*authenticator) AuthenticateBlobRequest(header *core.BlobHeader, signature
5464
}
5565

5666
// AuthenticatePaymentStateRequest verifies the signature of the payment state request
57-
// The signature is signed over the byte representation of the account ID
67+
// The signature is signed over the byte representation of the account ID and requestHash
5868
// See implementation of BlobRequestSigner.SignPaymentStateRequest for more details
5969
func (a *authenticator) AuthenticatePaymentStateRequest(accountAddr common.Address, request *pb.GetPaymentStateRequest) error {
6070
sig := request.GetSignature()
@@ -63,10 +73,14 @@ func (a *authenticator) AuthenticatePaymentStateRequest(accountAddr common.Addre
6373
return fmt.Errorf("signature length is unexpected: %d", len(sig))
6474
}
6575

66-
timestamp := request.GetTimestamp()
76+
requestHash, err := hashing.HashGetPaymentStateRequestFromRequest(request)
77+
if err != nil {
78+
return fmt.Errorf("failed to hash request: %w", err)
79+
}
80+
accountAddrWithHash := append(accountAddr.Bytes(), requestHash...)
81+
hash := sha256.Sum256(accountAddrWithHash)
6782

6883
// Verify the signature
69-
hash := sha256.Sum256(accountAddr.Bytes())
7084
sigPublicKeyECDSA, err := crypto.SigToPub(hash[:], sig)
7185
if err != nil {
7286
return fmt.Errorf("failed to recover public key from signature: %v", err)
@@ -78,12 +92,8 @@ func (a *authenticator) AuthenticatePaymentStateRequest(accountAddr common.Addre
7892
return errors.New("signature doesn't match with provided public key")
7993
}
8094

81-
requestHash, err := hashing.HashGetPaymentStateRequest(request)
82-
if err != nil {
83-
return fmt.Errorf("failed to hash request: %w", err)
84-
}
85-
86-
if err := a.replayGuardian.VerifyRequest(requestHash, time.Unix(0, int64(timestamp))); err != nil {
95+
timestamp := request.GetTimestamp()
96+
if err := a.ReplayGuardian.VerifyRequest(requestHash, time.Unix(0, int64(timestamp))); err != nil {
8797
return fmt.Errorf("failed to verify request: %v", err)
8898
}
8999

core/auth/v2/signer.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"crypto/ecdsa"
55
"crypto/sha256"
66
"fmt"
7+
"github.com/Layr-Labs/eigenda/api/hashing"
78

89
core "github.com/Layr-Labs/eigenda/core/v2"
910
"github.com/ethereum/go-ethereum/common"
@@ -44,13 +45,19 @@ func (s *LocalBlobRequestSigner) SignBlobRequest(header *core.BlobHeader) ([]byt
4445
return sig, nil
4546
}
4647

47-
func (s *LocalBlobRequestSigner) SignPaymentStateRequest() ([]byte, error) {
48+
func (s *LocalBlobRequestSigner) SignPaymentStateRequest(timestamp uint64) ([]byte, error) {
4849
accountId, err := s.GetAccountID()
4950
if err != nil {
5051
return nil, fmt.Errorf("failed to get account ID: %v", err)
5152
}
5253

53-
hash := sha256.Sum256(accountId.Bytes())
54+
requestHash, err := hashing.HashGetPaymentStateRequestFromFields(accountId, timestamp)
55+
if err != nil {
56+
return nil, fmt.Errorf("failed to hash request: %w", err)
57+
}
58+
59+
accountIdWithHash := append(accountId.Bytes(), requestHash...)
60+
hash := sha256.Sum256(accountIdWithHash)
5461
// Sign the account ID using the private key
5562
sig, err := crypto.Sign(hash[:], s.PrivateKey)
5663
if err != nil {
@@ -77,7 +84,7 @@ func (s *LocalNoopSigner) SignBlobRequest(header *core.BlobHeader) ([]byte, erro
7784
return nil, fmt.Errorf("noop signer cannot sign blob request")
7885
}
7986

80-
func (s *LocalNoopSigner) SignPaymentStateRequest() ([]byte, error) {
87+
func (s *LocalNoopSigner) SignPaymentStateRequest(timestamp uint64) ([]byte, error) {
8188
return nil, fmt.Errorf("noop signer cannot sign payment state request")
8289
}
8390

core/auth/v2/signer_test.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package v2
22

33
import (
44
"crypto/sha256"
5+
"github.com/Layr-Labs/eigenda/api/hashing"
56
"math/big"
67
"testing"
8+
"time"
79

810
corev1 "github.com/Layr-Labs/eigenda/core"
911
core "github.com/Layr-Labs/eigenda/core/v2"
@@ -108,16 +110,20 @@ func TestSignPaymentStateRequest(t *testing.T) {
108110
accountID, err := signer.GetAccountID()
109111
require.NoError(t, err)
110112

111-
signature, err := signer.SignPaymentStateRequest()
113+
fixedTimestamp := uint64(1609459200000000000)
114+
signature, err := signer.SignPaymentStateRequest(fixedTimestamp)
112115
require.NoError(t, err)
113116
require.NotNil(t, signature)
114117

115-
hash := sha256.Sum256(accountID.Bytes())
116-
// Recover the public key from the signature
118+
requestHash, err := hashing.HashGetPaymentStateRequestFromFields(accountID, fixedTimestamp)
119+
require.NoError(t, err)
120+
121+
accountIdWithHash := append(accountID.Bytes(), requestHash...)
122+
hash := sha256.Sum256(accountIdWithHash)
123+
117124
pubKey, err := crypto.SigToPub(hash[:], signature)
118125
require.NoError(t, err)
119126

120-
// Verify that the recovered address matches the signer's address
121127
recoveredAddr := crypto.PubkeyToAddress(*pubKey).Hex()
122128
assert.Equal(t, expectedAddr, recoveredAddr)
123129
}
@@ -133,7 +139,7 @@ func TestNoopSigner(t *testing.T) {
133139
})
134140

135141
t.Run("SignPaymentStateRequest", func(t *testing.T) {
136-
sig, err := signer.SignPaymentStateRequest()
142+
sig, err := signer.SignPaymentStateRequest(uint64(time.Now().UnixNano()))
137143
assert.Error(t, err)
138144
assert.Nil(t, sig)
139145
assert.Equal(t, "noop signer cannot sign payment state request", err.Error())

core/v2/auth.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ type BlobRequestAuthenticator interface {
1212

1313
type BlobRequestSigner interface {
1414
SignBlobRequest(header *BlobHeader) ([]byte, error)
15-
SignPaymentStateRequest() ([]byte, error)
15+
SignPaymentStateRequest(timestamp uint64) ([]byte, error)
1616
GetAccountID() (gethcommon.Address, error)
1717
}

disperser/apiserver/disperse_blob_v2.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ func (s *DispersalServerV2) validateDispersalRequest(
209209
return nil, fmt.Errorf("invalid blob version %d; valid blob versions are: %v", blobHeaderProto.GetVersion(), onchainState.BlobVersionParameters.Keys())
210210
}
211211

212-
if err = s.authenticator.AuthenticateBlobRequest(blobHeader, signature); err != nil {
212+
if err = s.blobRequestAuthenticator.AuthenticateBlobRequest(blobHeader, signature); err != nil {
213213
return nil, fmt.Errorf("authentication failed: %w", err)
214214
}
215215

0 commit comments

Comments
 (0)