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

Disperser auth innabox #1052

Draft
wants to merge 94 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
0936f5f
Enable TLS connections between the disperser and the DA nodes.
cody-littley Dec 4, 2024
d36424e
Incremental progress.
cody-littley Dec 4, 2024
342d500
Moar cowsay
cody-littley Dec 4, 2024
fd2ab69
formatting
cody-littley Dec 4, 2024
e2e3fef
Started working on unit test.
cody-littley Dec 4, 2024
4373095
Incremental progress.
cody-littley Dec 4, 2024
df38980
It works now, kind of
cody-littley Dec 4, 2024
d54c7f7
Incremental progress.
cody-littley Dec 5, 2024
083d67d
Start experimenting with ecdsa.
cody-littley Dec 6, 2024
0a5c91a
Authorize StoreChunks() requests.
cody-littley Dec 10, 2024
8e9c16b
Merge branch 'master' into disperser-auth
cody-littley Dec 10, 2024
f348550
Update docs.
cody-littley Dec 10, 2024
01b7ece
Incremental progress.
cody-littley Dec 11, 2024
e09a77c
Incremental progress.
cody-littley Dec 11, 2024
290c93c
Incremental progress.
cody-littley Dec 11, 2024
5ba4b7b
Incremental progress.
cody-littley Dec 11, 2024
e094371
Added test placeholder.
cody-littley Dec 11, 2024
db0885a
shuffle stuff around
cody-littley Dec 12, 2024
da2ca6c
Merge branch 'master' into disperser-auth
cody-littley Dec 12, 2024
b04fcfa
Unit tests for request signing.
cody-littley Dec 12, 2024
29e7e7d
Delete stale code.
cody-littley Dec 12, 2024
fb1ebfb
Get things kind of working
cody-littley Dec 12, 2024
6ca7c9b
Finished unit tests.
cody-littley Dec 13, 2024
3348883
Cleanup.
cody-littley Dec 13, 2024
e5966f0
Cleanup
cody-littley Dec 13, 2024
f28a5d4
Merge branch 'master' into disperser-auth
cody-littley Dec 16, 2024
f0c6f4e
Cleanup.
cody-littley Dec 16, 2024
6a00256
Added request signer.
cody-littley Dec 16, 2024
27d5b8a
Incremental progress.
cody-littley Dec 16, 2024
bbd82ec
Update flags.
cody-littley Dec 16, 2024
157c2ea
Started work on unit test, not yet working.
cody-littley Dec 16, 2024
d793501
Incremental progress in kludging something together
cody-littley Dec 16, 2024
b81e430
Incremental test iteration.
cody-littley Dec 16, 2024
7327db1
Added debug printing.
cody-littley Dec 17, 2024
8cfa971
IT'S ALIVE!
cody-littley Dec 17, 2024
306746e
Partial cleanup.
cody-littley Dec 17, 2024
b41d729
Move code to proper locations.
cody-littley Dec 17, 2024
d709520
Copy eigensdk-go implementation of kms parsing.
cody-littley Dec 17, 2024
2e44b02
Update kms.go
anupsv Dec 18, 2024
aebda81
Create kms_fuzz_test.go
anupsv Dec 18, 2024
260bf79
Update Makefile for fuzz tests
anupsv Dec 18, 2024
dbd0d24
Merge pull request #1 from anupsv/disperser-auth
cody-littley Dec 18, 2024
5dbb178
Merge branch 'master' into disperser-auth
cody-littley Dec 18, 2024
dcd3e06
Disable request signing if KMS key name is not provided.
cody-littley Dec 18, 2024
11cf45a
Merge branch 'master' into disperser-auth
cody-littley Dec 18, 2024
3580bed
Merge branch 'master' into disperser-auth
cody-littley Dec 18, 2024
f6b008f
Tie into bindings.
cody-littley Dec 18, 2024
1493ded
Incremental progress.
cody-littley Dec 18, 2024
feab7a1
Merge branch 'master' into disperser-auth
cody-littley Dec 19, 2024
52f2354
Add debug code.
cody-littley Dec 19, 2024
7c5cc93
Fix unit test.
cody-littley Dec 19, 2024
c83db79
Disable request signing for inabox e2e test.
cody-littley Dec 19, 2024
e5cb69c
Added flag for disabling signing.
cody-littley Dec 19, 2024
20216c4
Cleanup.
cody-littley Dec 19, 2024
3c325ef
Made suggested changes.
cody-littley Dec 20, 2024
d4b7051
Start messing around with external KMS keys.
cody-littley Dec 20, 2024
02ed38a
tweak docker build
cody-littley Dec 20, 2024
60ad5c9
Merge branch 'disperser-auth' into disperser-auth-innabox
cody-littley Dec 20, 2024
8560b8b
Remove test.
cody-littley Dec 20, 2024
613bd20
Generate kms key pair.
cody-littley Dec 20, 2024
4912547
Fix localstack url
cody-littley Dec 20, 2024
80c7e6d
Make requested changes.
cody-littley Dec 20, 2024
2b6cbb2
Made suggested changes.
cody-littley Dec 20, 2024
a9155a0
Merge branch 'master' into disperser-auth
cody-littley Jan 2, 2025
3a9792c
Merge branch 'disperser-auth' into disperser-auth-innabox
cody-littley Jan 2, 2025
a57140c
Add debug stack trace.
cody-littley Jan 2, 2025
11e2ffa
Tweak stack trace.
cody-littley Jan 2, 2025
ad89396
Tweak aws URL
cody-littley Jan 2, 2025
a2b602e
Manually start localstack.
cody-littley Jan 2, 2025
db018f0
Don't start localstack for churner test.
cody-littley Jan 2, 2025
d1ac4a8
Skip key generation when localstack is not running.
cody-littley Jan 2, 2025
822ab3d
Write code that sets disperser address.
cody-littley Jan 2, 2025
ee262bc
Enable StoreChunks() signing
cody-littley Jan 2, 2025
262e1a9
Change where the disperser address is written to contract.
cody-littley Jan 2, 2025
d32d0a8
Wait for disperser address transaction to be handled.
cody-littley Jan 3, 2025
1f42f9d
Actually submit transaction.
cody-littley Jan 3, 2025
7caac2c
Create a block every second.
cody-littley Jan 3, 2025
20fd4da
Use client with no confirmations.
cody-littley Jan 3, 2025
b09a43e
Revert anvil change
cody-littley Jan 3, 2025
6f9e242
Change where disperser address is updated.
cody-littley Jan 3, 2025
0a3e35b
Add stack trace.
cody-littley Jan 6, 2025
d92252d
Increase stack trace size.
cody-littley Jan 6, 2025
524b22a
Tweak stack trace.
cody-littley Jan 6, 2025
7971b13
Multiple log statements.
cody-littley Jan 6, 2025
e07b734
Update disperser address earlier.
cody-littley Jan 6, 2025
8452d09
Initiate logger earlier.
cody-littley Jan 6, 2025
525e37f
Reshuffle code.
cody-littley Jan 6, 2025
87a7610
Tweaks.
cody-littley Jan 6, 2025
3852dc7
Play with ordering.
cody-littley Jan 6, 2025
a305e93
Relocate code.
cody-littley Jan 6, 2025
568e971
Try moving disperser setup again.
cody-littley Jan 6, 2025
c77e596
move code
cody-littley Jan 6, 2025
7371497
Tweaks
cody-littley Jan 6, 2025
eca25b9
Use other eth client.
cody-littley Jan 6, 2025
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
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ RUN --mount=type=cache,target=/go/pkg/mod \

# Controller build stage
FROM common-builder AS controller-builder
COPY node/auth /app/node/auth
WORKDIR /app/disperser
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ dataapi-build:
unit-tests:
./test.sh

fuzz-tests:
go test --fuzz=FuzzParseSignatureKMS -fuzztime=5m ./common

integration-tests-churner:
go test -v ./churner/tests

Expand Down
30 changes: 30 additions & 0 deletions api/clients/mock/static_request_signer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package mock

import (
"context"
"crypto/ecdsa"
"github.com/Layr-Labs/eigenda/api/clients/v2"
v2 "github.com/Layr-Labs/eigenda/api/grpc/node/v2"
"github.com/Layr-Labs/eigenda/node/auth"
)

var _ clients.DispersalRequestSigner = &staticRequestSigner{}

// StaticRequestSigner is a DispersalRequestSigner that signs requests with a static key (i.e. it doesn't use AWS KMS).
// Useful for testing.
type staticRequestSigner struct {
key *ecdsa.PrivateKey
}

func NewStaticRequestSigner(key *ecdsa.PrivateKey) clients.DispersalRequestSigner {
return &staticRequestSigner{
key: key,
}
}

func (s *staticRequestSigner) SignStoreChunksRequest(
ctx context.Context,
request *v2.StoreChunksRequest) ([]byte, error) {

return auth.SignStoreChunksRequest(s.key, request)
}
62 changes: 62 additions & 0 deletions api/clients/v2/dispersal_request_signer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package clients

import (
"context"
"crypto/ecdsa"
"fmt"
grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2"
"github.com/Layr-Labs/eigenda/api/hashing"
"github.com/Layr-Labs/eigenda/common"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/kms"
)

// DispersalRequestSigner encapsulates the logic for signing GetChunks requests.
type DispersalRequestSigner interface {
// SignStoreChunksRequest signs a StoreChunksRequest. Does not modify the request
// (i.e. it does not insert the signature).
SignStoreChunksRequest(ctx context.Context, request *grpc.StoreChunksRequest) ([]byte, error)
}

var _ DispersalRequestSigner = &requestSigner{}

type requestSigner struct {
keyID string
publicKey *ecdsa.PublicKey
keyManager *kms.Client
}

// NewDispersalRequestSigner creates a new DispersalRequestSigner.
func NewDispersalRequestSigner(
ctx context.Context,
region string,
endpoint string,
keyID string) (DispersalRequestSigner, error) {

keyManager := kms.New(kms.Options{
Region: region,
BaseEndpoint: aws.String(endpoint),
})

key, err := common.LoadPublicKeyKMS(ctx, keyManager, keyID)
if err != nil {
return nil, fmt.Errorf("failed to get ecdsa public key: %w", err)
}

return &requestSigner{
keyID: keyID,
publicKey: key,
keyManager: keyManager,
}, nil
}

func (s *requestSigner) SignStoreChunksRequest(ctx context.Context, request *grpc.StoreChunksRequest) ([]byte, error) {
hash := hashing.HashStoreChunksRequest(request)

signature, err := common.SignKMS(ctx, s.keyManager, s.keyID, s.publicKey, hash)
if err != nil {
return nil, fmt.Errorf("failed to sign request: %w", err)
}

return signature, nil
}
129 changes: 129 additions & 0 deletions api/clients/v2/dispersal_request_signer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package clients

import (
"context"
"github.com/Layr-Labs/eigenda/common"
"github.com/Layr-Labs/eigenda/common/testutils/random"
"github.com/Layr-Labs/eigenda/inabox/deploy"
"github.com/Layr-Labs/eigenda/node/auth"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/kms"
"github.com/aws/aws-sdk-go-v2/service/kms/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ory/dockertest/v3"
"github.com/stretchr/testify/require"
"log"
"os"
"path/filepath"
"runtime"
"testing"
)

var (
dockertestPool *dockertest.Pool
dockertestResource *dockertest.Resource
)

const (
localstackPort = "4570"
localstackHost = "http://0.0.0.0:4570"
region = "us-east-1"
)

func setup(t *testing.T) {
deployLocalStack := !(os.Getenv("DEPLOY_LOCALSTACK") == "false")

_, b, _, _ := runtime.Caller(0)
rootPath := filepath.Join(filepath.Dir(b), "../../..")
changeDirectory(filepath.Join(rootPath, "inabox"))

if deployLocalStack {
var err error
dockertestPool, dockertestResource, err = deploy.StartDockertestWithLocalstackContainer(localstackPort)
require.NoError(t, err)
}
}

func changeDirectory(path string) {
err := os.Chdir(path)
if err != nil {

currentDirectory, err := os.Getwd()
if err != nil {
log.Printf("Failed to get current directory. Error: %s", err)
}

log.Panicf("Failed to change directories. CWD: %s, Error: %s", currentDirectory, err)
}

newDir, err := os.Getwd()
if err != nil {
log.Panicf("Failed to get working directory. Error: %s", err)
}
log.Printf("Current Working Directory: %s\n", newDir)
}

func teardown() {
deployLocalStack := !(os.Getenv("DEPLOY_LOCALSTACK") == "false")

if deployLocalStack {
deploy.PurgeDockertestResources(dockertestPool, dockertestResource)
}
}

func TestRequestSigning(t *testing.T) {
rand := random.NewTestRandom(t)
setup(t)
defer teardown()

keyManager := kms.New(kms.Options{
Region: region,
BaseEndpoint: aws.String(localstackHost),
})

for i := 0; i < 10; i++ {
createKeyOutput, err := keyManager.CreateKey(context.Background(), &kms.CreateKeyInput{
KeySpec: types.KeySpecEccSecgP256k1,
KeyUsage: types.KeyUsageTypeSignVerify,
})
require.NoError(t, err)

keyID := *createKeyOutput.KeyMetadata.KeyId

key, err := common.LoadPublicKeyKMS(context.Background(), keyManager, keyID)
require.NoError(t, err)

publicAddress := crypto.PubkeyToAddress(*key)

for j := 0; j < 10; j++ {
request := auth.RandomStoreChunksRequest(rand)
request.Signature = nil

signer, err := NewDispersalRequestSigner(context.Background(), region, localstackHost, keyID)
require.NoError(t, err)

// Test a valid signature.
signature, err := signer.SignStoreChunksRequest(context.Background(), request)
require.NoError(t, err)

require.Nil(t, request.Signature)
request.Signature = signature
err = auth.VerifyStoreChunksRequest(publicAddress, request)
require.NoError(t, err)

// Changing a byte in the middle of the signature should make the verification fail
badSignature := make([]byte, len(signature))
copy(badSignature, signature)
badSignature[10] = badSignature[10] + 1
request.Signature = badSignature
err = auth.VerifyStoreChunksRequest(publicAddress, request)
require.Error(t, err)

// Changing a byte in the middle of the request should make the verification fail
request.DisperserID = request.DisperserID + 1
request.Signature = signature
err = auth.VerifyStoreChunksRequest(publicAddress, request)
require.Error(t, err)
}
}
}
31 changes: 23 additions & 8 deletions api/clients/v2/node_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package clients
import (
"context"
"fmt"
"github.com/Layr-Labs/eigenda/api"
"sync"

commonpb "github.com/Layr-Labs/eigenda/api/grpc/common/v2"
Expand All @@ -24,21 +25,23 @@ type NodeClient interface {
}

type nodeClient struct {
config *NodeClientConfig
initOnce sync.Once
conn *grpc.ClientConn
config *NodeClientConfig
initOnce sync.Once
conn *grpc.ClientConn
requestSigner DispersalRequestSigner

dispersalClient nodegrpc.DispersalClient
}

var _ NodeClient = (*nodeClient)(nil)

func NewNodeClient(config *NodeClientConfig) (*nodeClient, error) {
func NewNodeClient(config *NodeClientConfig, requestSigner DispersalRequestSigner) (NodeClient, error) {
if config == nil || config.Hostname == "" || config.Port == "" {
return nil, fmt.Errorf("invalid config: %v", config)
}
return &nodeClient{
config: config,
config: config,
requestSigner: requestSigner,
}, nil
}

Expand All @@ -60,16 +63,28 @@ func (c *nodeClient) StoreChunks(ctx context.Context, batch *corev2.Batch) (*cor
}
}

// Call the gRPC method to store chunks
response, err := c.dispersalClient.StoreChunks(ctx, &nodegrpc.StoreChunksRequest{
request := &nodegrpc.StoreChunksRequest{
Batch: &commonpb.Batch{
Header: &commonpb.BatchHeader{
BatchRoot: batch.BatchHeader.BatchRoot[:],
ReferenceBlockNumber: batch.BatchHeader.ReferenceBlockNumber,
},
BlobCertificates: blobCerts,
},
})
DisperserID: api.EigenLabsDisperserID, // this will need to be updated when dispersers are decentralized
}

if c.requestSigner != nil {
// Sign the request to store chunks
signature, err := c.requestSigner.SignStoreChunksRequest(ctx, request)
if err != nil {
return nil, fmt.Errorf("failed to sign store chunks request: %v", err)
}
request.Signature = signature
}

// Call the gRPC method to store chunks
response, err := c.dispersalClient.StoreChunks(ctx, request)
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions api/clients/v2/relay_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"context"
"errors"
"fmt"
"github.com/Layr-Labs/eigenda/api/hashing"
"github.com/Layr-Labs/eigenda/core"
"github.com/Layr-Labs/eigenda/relay/auth"
"sync"

relaygrpc "github.com/Layr-Labs/eigenda/api/grpc/relay"
Expand Down Expand Up @@ -115,7 +115,7 @@ func (c *relayClient) signGetChunksRequest(ctx context.Context, request *relaygr
return errors.New("no message signer provided in config, cannot sign get chunks request")
}

hash := auth.HashGetChunksRequest(request)
hash := hashing.HashGetChunksRequest(request)
hashArray := [32]byte{}
copy(hashArray[:], hash)
signature, err := c.config.MessageSigner(ctx, hashArray)
Expand Down
4 changes: 4 additions & 0 deletions api/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package api

// EigenLabsDisperserID is the ID of the disperser that is managed by Eigen Labs.
const EigenLabsDisperserID = uint32(0)
42 changes: 41 additions & 1 deletion api/docs/eigenda-protos.html
Original file line number Diff line number Diff line change
Expand Up @@ -3494,7 +3494,7 @@ <h3 id="node.v2.StoreChunksReply">StoreChunksReply</h3>


<h3 id="node.v2.StoreChunksRequest">StoreChunksRequest</h3>
<p></p>
<p>Request that the Node store a batch of chunks.</p>


<table class="field-table">
Expand All @@ -3510,6 +3510,46 @@ <h3 id="node.v2.StoreChunksRequest">StoreChunksRequest</h3>
<td><p>batch of blobs to store </p></td>
</tr>

<tr>
<td>disperserID</td>
<td><a href="#uint32">uint32</a></td>
<td></td>
<td><p>ID of the disperser that is requesting the storage of the batch. </p></td>
</tr>

<tr>
<td>signature</td>
<td><a href="#bytes">bytes</a></td>
<td></td>
<td><p>Signature using the disperser&#39;s ECDSA key over keccak hash of the batch. The purpose of this signature
is to prevent hooligans from tricking DA nodes into storing data that they shouldn&#39;t be storing.

Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned).
A reference implementation (golang) can be found at
https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go

1. digest batch.BatchHeader.BatchRoot
2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian)
3. for each certificate in batch.BlobCertificates:
a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian)
b. for each quorum_number in certificate.BlobHeader.QuorumNumbers:
i. digest quorum_number (4 bytes, unsigned big endian)
c. digest certificate.BlobHeader.Commitment.Commitment
d. digest certificate.BlobHeader.Commitment.LengthCommitment
e. digest certificate.BlobHeader.Commitment.LengthProof
f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian)
g. digest certificate.BlobHeader.PaymentHeader.AccountId
h. digest certificate.BlobHeader.PaymentHeader.ReservationPeriod (4 bytes, unsigned big endian)
i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment
j. digest certificate.BlobHeader.PaymentHeader.Salt (4 bytes, unsigned big endian)
k. digest certificate.BlobHeader.Signature
l. for each relay in certificate.Relays:
i. digest relay (4 bytes, unsigned big endian)
4. digest disperserID (4 bytes, unsigned big endian)

Note that this signature is not included in the hash for obvious reasons. </p></td>
</tr>

</tbody>
</table>

Expand Down
Loading
Loading