Add functions for signing and verifying during bandwidth exchange (#246)
* Added initial functions for signing and verifying * whoops * Get client up to speed * Added initial functions for signing and verifying * whoops * Get client up to speed * wip * wip * actual signatures in tests (cherry picked from commit 1464853b737f1d712d64fbf90147f535525c8fd9) * bugfixing * Generate private key in example * Generate signatures for pieceranger tests * Update examples to use TLS * Use private key from identity inside of example * Use crypto.PrivateKey interface * Change err name in defers * Pass tests * Pass identity Key to PSClient * Get tests passing on travis * Resolve linter complaints
This commit is contained in:
parent
75de358740
commit
899e1e68f1
@ -5,6 +5,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
@ -17,23 +18,39 @@ import (
|
|||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"storj.io/storj/pkg/piecestore/rpc/client"
|
"storj.io/storj/pkg/piecestore/rpc/client"
|
||||||
|
"storj.io/storj/pkg/provider"
|
||||||
pb "storj.io/storj/protos/piecestore"
|
pb "storj.io/storj/protos/piecestore"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ctx = context.Background()
|
||||||
var argError = errs.Class("argError")
|
var argError = errs.Class("argError")
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
|
|
||||||
|
ca, err := provider.NewCA(ctx, 12, 4)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
identity, err := ca.NewIdentity()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
identOpt, err := identity.DialOption()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Set up connection with rpc server
|
// Set up connection with rpc server
|
||||||
var conn *grpc.ClientConn
|
var conn *grpc.ClientConn
|
||||||
conn, err := grpc.Dial(":7777", grpc.WithInsecure())
|
conn, err = grpc.Dial(":7777", identOpt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("did not connect: %s", err)
|
log.Fatalf("did not connect: %s", err)
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
psClient, err := client.NewPSClient(conn, 1024*32)
|
|
||||||
|
psClient, err := client.NewPSClient(conn, 1024*32, identity.Key.(*ecdsa.PrivateKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("could not initialize PSClient: %s", err)
|
log.Fatalf("could not initialize PSClient: %s", err)
|
||||||
}
|
}
|
||||||
@ -127,7 +144,6 @@ func main() {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
rr, err := psClient.Get(ctx, client.PieceID(c.Args().Get(id)), pieceInfo.Size, &pb.PayerBandwidthAllocation{})
|
rr, err := psClient.Get(ctx, client.PieceID(c.Args().Get(id)), pieceInfo.Size, &pb.PayerBandwidthAllocation{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to retrieve file of id: %s\n", c.Args().Get(id))
|
fmt.Printf("Failed to retrieve file of id: %s\n", c.Args().Get(id))
|
||||||
|
@ -130,7 +130,7 @@ func (c Config) NewGateway(ctx context.Context,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ec := ecclient.NewClient(t, c.MaxBufferMem)
|
ec := ecclient.NewClient(identity, t, c.MaxBufferMem)
|
||||||
fc, err := infectious.NewFEC(c.MinThreshold, c.MaxThreshold)
|
fc, err := infectious.NewFEC(c.MinThreshold, c.MaxThreshold)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -5,6 +5,8 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -16,6 +18,8 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
|
"github.com/gtank/cryptopasta"
|
||||||
|
|
||||||
"storj.io/storj/pkg/ranger"
|
"storj.io/storj/pkg/ranger"
|
||||||
pb "storj.io/storj/protos/piecestore"
|
pb "storj.io/storj/protos/piecestore"
|
||||||
)
|
)
|
||||||
@ -45,11 +49,12 @@ type PSClient interface {
|
|||||||
type Client struct {
|
type Client struct {
|
||||||
route pb.PieceStoreRoutesClient
|
route pb.PieceStoreRoutesClient
|
||||||
conn *grpc.ClientConn
|
conn *grpc.ClientConn
|
||||||
|
prikey crypto.PrivateKey
|
||||||
bandwidthMsgSize int
|
bandwidthMsgSize int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPSClient initilizes a PSClient
|
// NewPSClient initilizes a PSClient
|
||||||
func NewPSClient(conn *grpc.ClientConn, bandwidthMsgSize int) (PSClient, error) {
|
func NewPSClient(conn *grpc.ClientConn, bandwidthMsgSize int, prikey crypto.PrivateKey) (PSClient, error) {
|
||||||
if bandwidthMsgSize < 0 || bandwidthMsgSize > *maxBandwidthMsgSize {
|
if bandwidthMsgSize < 0 || bandwidthMsgSize > *maxBandwidthMsgSize {
|
||||||
return nil, ClientError.New(fmt.Sprintf("Invalid Bandwidth Message Size: %v", bandwidthMsgSize))
|
return nil, ClientError.New(fmt.Sprintf("Invalid Bandwidth Message Size: %v", bandwidthMsgSize))
|
||||||
}
|
}
|
||||||
@ -62,11 +67,12 @@ func NewPSClient(conn *grpc.ClientConn, bandwidthMsgSize int) (PSClient, error)
|
|||||||
conn: conn,
|
conn: conn,
|
||||||
route: pb.NewPieceStoreRoutesClient(conn),
|
route: pb.NewPieceStoreRoutesClient(conn),
|
||||||
bandwidthMsgSize: bandwidthMsgSize,
|
bandwidthMsgSize: bandwidthMsgSize,
|
||||||
|
prikey: prikey,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCustomRoute creates new Client with custom route interface
|
// NewCustomRoute creates new Client with custom route interface
|
||||||
func NewCustomRoute(route pb.PieceStoreRoutesClient, bandwidthMsgSize int) (*Client, error) {
|
func NewCustomRoute(route pb.PieceStoreRoutesClient, bandwidthMsgSize int, prikey crypto.PrivateKey) (*Client, error) {
|
||||||
if bandwidthMsgSize < 0 || bandwidthMsgSize > *maxBandwidthMsgSize {
|
if bandwidthMsgSize < 0 || bandwidthMsgSize > *maxBandwidthMsgSize {
|
||||||
return nil, ClientError.New(fmt.Sprintf("Invalid Bandwidth Message Size: %v", bandwidthMsgSize))
|
return nil, ClientError.New(fmt.Sprintf("Invalid Bandwidth Message Size: %v", bandwidthMsgSize))
|
||||||
}
|
}
|
||||||
@ -78,6 +84,7 @@ func NewCustomRoute(route pb.PieceStoreRoutesClient, bandwidthMsgSize int) (*Cli
|
|||||||
return &Client{
|
return &Client{
|
||||||
route: route,
|
route: route,
|
||||||
bandwidthMsgSize: bandwidthMsgSize,
|
bandwidthMsgSize: bandwidthMsgSize,
|
||||||
|
prikey: prikey,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +159,10 @@ func (client *Client) Delete(ctx context.Context, id PieceID) error {
|
|||||||
|
|
||||||
// sign a message using the clients private key
|
// sign a message using the clients private key
|
||||||
func (client *Client) sign(msg []byte) (signature []byte, err error) {
|
func (client *Client) sign(msg []byte) (signature []byte, err error) {
|
||||||
// use c.pkey to sign msg
|
if client.prikey == nil {
|
||||||
|
return nil, ClientError.New("Failed to sign msg: Private Key not Set")
|
||||||
|
}
|
||||||
|
|
||||||
return signature, err
|
// use c.pkey to sign msg
|
||||||
|
return cryptopasta.Sign(msg, client.prikey.(*ecdsa.PrivateKey))
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,14 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/gogo/protobuf/proto"
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
@ -42,6 +44,9 @@ func TestPieceRanger(t *testing.T) {
|
|||||||
} {
|
} {
|
||||||
errTag := fmt.Sprintf("Test case #%d", i)
|
errTag := fmt.Sprintf("Test case #%d", i)
|
||||||
|
|
||||||
|
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
route := pb.NewMockPieceStoreRoutesClient(ctrl)
|
route := pb.NewMockPieceStoreRoutesClient(ctrl)
|
||||||
|
|
||||||
calls := []*gomock.Call{
|
calls := []*gomock.Call{
|
||||||
@ -54,46 +59,29 @@ func TestPieceRanger(t *testing.T) {
|
|||||||
pid := NewPieceID()
|
pid := NewPieceID()
|
||||||
|
|
||||||
if tt.offset >= 0 && tt.length > 0 && tt.offset+tt.length <= tt.size {
|
if tt.offset >= 0 && tt.length > 0 && tt.offset+tt.length <= tt.size {
|
||||||
|
msg1 := &pb.PieceRetrieval{
|
||||||
|
PieceData: &pb.PieceRetrieval_PieceData{
|
||||||
|
Id: pid.String(), Size: tt.length, Offset: tt.offset,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
calls = append(calls,
|
calls = append(calls,
|
||||||
stream.EXPECT().Send(
|
stream.EXPECT().Send(msg1).Return(nil),
|
||||||
&pb.PieceRetrieval{
|
stream.EXPECT().Send(gomock.Any()).Return(nil),
|
||||||
PieceData: &pb.PieceRetrieval_PieceData{
|
|
||||||
Id: pid.String(), Size: tt.length, Offset: tt.offset,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
).Return(nil),
|
|
||||||
stream.EXPECT().Send(
|
|
||||||
&pb.PieceRetrieval{
|
|
||||||
Bandwidthallocation: &pb.RenterBandwidthAllocation{
|
|
||||||
Data: serializeData(&pb.RenterBandwidthAllocation_Data{
|
|
||||||
PayerAllocation: &pb.PayerBandwidthAllocation{},
|
|
||||||
Total: 32 * 1024,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
).Return(nil),
|
|
||||||
stream.EXPECT().Recv().Return(
|
stream.EXPECT().Recv().Return(
|
||||||
&pb.PieceRetrievalStream{
|
&pb.PieceRetrievalStream{
|
||||||
Size: tt.length,
|
Size: tt.length,
|
||||||
Content: []byte(tt.data)[tt.offset : tt.offset+tt.length],
|
Content: []byte(tt.data)[tt.offset : tt.offset+tt.length],
|
||||||
}, nil),
|
}, nil),
|
||||||
stream.EXPECT().Send(
|
stream.EXPECT().Send(gomock.Any()).Return(nil),
|
||||||
&pb.PieceRetrieval{
|
|
||||||
Bandwidthallocation: &pb.RenterBandwidthAllocation{
|
|
||||||
Data: serializeData(&pb.RenterBandwidthAllocation_Data{
|
|
||||||
PayerAllocation: &pb.PayerBandwidthAllocation{},
|
|
||||||
Total: 32 * 1024 * 2,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
).Return(nil),
|
|
||||||
stream.EXPECT().Recv().Return(&pb.PieceRetrievalStream{}, io.EOF),
|
stream.EXPECT().Recv().Return(&pb.PieceRetrievalStream{}, io.EOF),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
gomock.InOrder(calls...)
|
gomock.InOrder(calls...)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
c, err := NewCustomRoute(route, 32*1024)
|
|
||||||
|
c, err := NewCustomRoute(route, 32*1024, priv)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
rr, err := PieceRanger(ctx, c, stream, pid, &pb.PayerBandwidthAllocation{})
|
rr, err := PieceRanger(ctx, c, stream, pid, &pb.PayerBandwidthAllocation{})
|
||||||
if assert.NoError(t, err, errTag) {
|
if assert.NoError(t, err, errTag) {
|
||||||
@ -142,45 +130,32 @@ func TestPieceRangerSize(t *testing.T) {
|
|||||||
|
|
||||||
stream := pb.NewMockPieceStoreRoutes_RetrieveClient(ctrl)
|
stream := pb.NewMockPieceStoreRoutes_RetrieveClient(ctrl)
|
||||||
|
|
||||||
|
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
if tt.offset >= 0 && tt.length > 0 && tt.offset+tt.length <= tt.size {
|
if tt.offset >= 0 && tt.length > 0 && tt.offset+tt.length <= tt.size {
|
||||||
|
msg1 := &pb.PieceRetrieval{
|
||||||
|
PieceData: &pb.PieceRetrieval_PieceData{
|
||||||
|
Id: pid.String(), Size: tt.length, Offset: tt.offset,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
gomock.InOrder(
|
gomock.InOrder(
|
||||||
stream.EXPECT().Send(
|
stream.EXPECT().Send(msg1).Return(nil),
|
||||||
&pb.PieceRetrieval{
|
stream.EXPECT().Send(gomock.Any()).Return(nil),
|
||||||
PieceData: &pb.PieceRetrieval_PieceData{
|
|
||||||
Id: pid.String(), Size: tt.length, Offset: tt.offset,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
).Return(nil),
|
|
||||||
stream.EXPECT().Send(
|
|
||||||
&pb.PieceRetrieval{Bandwidthallocation: &pb.RenterBandwidthAllocation{
|
|
||||||
Data: serializeData(&pb.RenterBandwidthAllocation_Data{
|
|
||||||
PayerAllocation: &pb.PayerBandwidthAllocation{},
|
|
||||||
Total: 32 * 1024,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
).Return(nil),
|
|
||||||
stream.EXPECT().Recv().Return(
|
stream.EXPECT().Recv().Return(
|
||||||
&pb.PieceRetrievalStream{
|
&pb.PieceRetrievalStream{
|
||||||
Size: tt.length,
|
Size: tt.length,
|
||||||
Content: []byte(tt.data)[tt.offset : tt.offset+tt.length],
|
Content: []byte(tt.data)[tt.offset : tt.offset+tt.length],
|
||||||
}, nil),
|
}, nil),
|
||||||
stream.EXPECT().Send(
|
stream.EXPECT().Send(gomock.Any()).Return(nil),
|
||||||
&pb.PieceRetrieval{
|
|
||||||
Bandwidthallocation: &pb.RenterBandwidthAllocation{
|
|
||||||
Data: serializeData(&pb.RenterBandwidthAllocation_Data{
|
|
||||||
PayerAllocation: &pb.PayerBandwidthAllocation{},
|
|
||||||
Total: 32 * 1024 * 2,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
).Return(nil),
|
|
||||||
stream.EXPECT().Recv().Return(&pb.PieceRetrievalStream{}, io.EOF),
|
stream.EXPECT().Recv().Return(&pb.PieceRetrievalStream{}, io.EOF),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
c, err := NewCustomRoute(route, 32*1024)
|
|
||||||
|
c, err := NewCustomRoute(route, 32*1024, priv)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
rr := PieceRangerSize(c, stream, pid, tt.size, &pb.PayerBandwidthAllocation{})
|
rr := PieceRangerSize(c, stream, pid, tt.size, &pb.PayerBandwidthAllocation{})
|
||||||
assert.Equal(t, tt.size, rr.Size(), errTag)
|
assert.Equal(t, tt.size, rr.Size(), errTag)
|
||||||
@ -196,9 +171,3 @@ func TestPieceRangerSize(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func serializeData(ba *pb.RenterBandwidthAllocation_Data) []byte {
|
|
||||||
data, _ := proto.Marshal(ba)
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
@ -51,7 +51,7 @@ func NewStreamReader(s *Server, stream pb.PieceStoreRoutes_StoreServer) *StreamR
|
|||||||
ba := recv.GetBandwidthallocation()
|
ba := recv.GetBandwidthallocation()
|
||||||
|
|
||||||
if ba != nil {
|
if ba != nil {
|
||||||
if err = s.verifySignature(ba); err != nil {
|
if err = s.verifySignature(stream.Context(), ba); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,9 +87,9 @@ func (s *Server) retrieveData(ctx context.Context, stream pb.PieceStoreRoutes_Re
|
|||||||
|
|
||||||
// Save latest bandwidth allocation even if we fail
|
// Save latest bandwidth allocation even if we fail
|
||||||
defer func() {
|
defer func() {
|
||||||
err := s.DB.WriteBandwidthAllocToDB(latestBA)
|
baWriteErr := s.DB.WriteBandwidthAllocToDB(latestBA)
|
||||||
if latestBA != nil && err != nil {
|
if latestBA != nil && baWriteErr != nil {
|
||||||
log.Println("WriteBandwidthAllocToDB Error:", err)
|
log.Println("WriteBandwidthAllocToDB Error:", baWriteErr)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ func (s *Server) retrieveData(ctx context.Context, stream pb.PieceStoreRoutes_Re
|
|||||||
return am.Used, am.TotalAllocated, err
|
return am.Used, am.TotalAllocated, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = s.verifySignature(ba); err != nil {
|
if err = s.verifySignature(ctx, ba); err != nil {
|
||||||
return am.Used, am.TotalAllocated, err
|
return am.Used, am.TotalAllocated, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,14 +4,19 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/gtank/cryptopasta"
|
||||||
|
"github.com/zeebo/errs"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
monkit "gopkg.in/spacemonkeygo/monkit.v2"
|
"gopkg.in/spacemonkeygo/monkit.v2"
|
||||||
|
|
||||||
|
"storj.io/storj/pkg/peertls"
|
||||||
"storj.io/storj/pkg/piecestore"
|
"storj.io/storj/pkg/piecestore"
|
||||||
"storj.io/storj/pkg/piecestore/rpc/server/psdb"
|
"storj.io/storj/pkg/piecestore/rpc/server/psdb"
|
||||||
"storj.io/storj/pkg/provider"
|
"storj.io/storj/pkg/provider"
|
||||||
@ -20,6 +25,9 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
mon = monkit.Package()
|
mon = monkit.Package()
|
||||||
|
|
||||||
|
// ServerError wraps errors returned from Server struct methods
|
||||||
|
ServerError = errs.Class("PSServer error")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config contains everything necessary for a server
|
// Config contains everything necessary for a server
|
||||||
@ -31,7 +39,7 @@ type Config struct {
|
|||||||
func (c Config) Run(ctx context.Context, server *provider.Provider) (err error) {
|
func (c Config) Run(ctx context.Context, server *provider.Provider) (err error) {
|
||||||
defer mon.Task()(&ctx)(&err)
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
s, err := Initialize(ctx, c)
|
s, err := Initialize(ctx, c, server.Identity().Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -54,10 +62,11 @@ func (c Config) Run(ctx context.Context, server *provider.Provider) (err error)
|
|||||||
type Server struct {
|
type Server struct {
|
||||||
DataDir string
|
DataDir string
|
||||||
DB *psdb.PSDB
|
DB *psdb.PSDB
|
||||||
|
pkey crypto.PrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize -- initializes a server struct
|
// Initialize -- initializes a server struct
|
||||||
func Initialize(ctx context.Context, config Config) (*Server, error) {
|
func Initialize(ctx context.Context, config Config, pkey crypto.PrivateKey) (*Server, error) {
|
||||||
dbPath := filepath.Join(config.Path, "piecestore.db")
|
dbPath := filepath.Join(config.Path, "piecestore.db")
|
||||||
dataDir := filepath.Join(config.Path, "piece-store-data")
|
dataDir := filepath.Join(config.Path, "piece-store-data")
|
||||||
|
|
||||||
@ -66,7 +75,7 @@ func Initialize(ctx context.Context, config Config) (*Server, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Server{DataDir: dataDir, DB: psDB}, nil
|
return &Server{DataDir: dataDir, DB: psDB, pkey: pkey}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop the piececstore node
|
// Stop the piececstore node
|
||||||
@ -124,12 +133,19 @@ func (s *Server) deleteByID(id string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) verifySignature(ba *pb.RenterBandwidthAllocation) error {
|
func (s *Server) verifySignature(ctx context.Context, ba *pb.RenterBandwidthAllocation) error {
|
||||||
// TODO: verify signature
|
pi, err := provider.PeerIdentityFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// data := ba.GetData()
|
k, ok := pi.Leaf.PublicKey.(*ecdsa.PublicKey)
|
||||||
// signature := ba.GetSignature()
|
if !ok {
|
||||||
log.Printf("Verified signature\n")
|
return peertls.ErrUnsupportedKey.New("%T", pi.Leaf.PublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := cryptopasta.Verify(ba.GetData(), ba.GetSignature(), k); !ok {
|
||||||
|
return ServerError.New("Failed to verify Signature")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -19,15 +21,15 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gogo/protobuf/proto"
|
"github.com/gogo/protobuf/proto"
|
||||||
|
"github.com/gtank/cryptopasta"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
pstore "storj.io/storj/pkg/piecestore"
|
pstore "storj.io/storj/pkg/piecestore"
|
||||||
"storj.io/storj/pkg/piecestore/rpc/server/psdb"
|
"storj.io/storj/pkg/piecestore/rpc/server/psdb"
|
||||||
|
"storj.io/storj/pkg/provider"
|
||||||
pb "storj.io/storj/protos/piecestore"
|
pb "storj.io/storj/protos/piecestore"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -48,7 +50,7 @@ func writeFileToDir(name, dir string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPiece(t *testing.T) {
|
func TestPiece(t *testing.T) {
|
||||||
TS := NewTestServer()
|
TS := NewTestServer(t)
|
||||||
defer TS.Stop()
|
defer TS.Stop()
|
||||||
|
|
||||||
if err := writeFileToDir("11111111111111111111", TS.s.DataDir); err != nil {
|
if err := writeFileToDir("11111111111111111111", TS.s.DataDir); err != nil {
|
||||||
@ -118,7 +120,7 @@ func TestPiece(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRetrieve(t *testing.T) {
|
func TestRetrieve(t *testing.T) {
|
||||||
TS := NewTestServer()
|
TS := NewTestServer(t)
|
||||||
defer TS.Stop()
|
defer TS.Stop()
|
||||||
|
|
||||||
// simulate piece stored with storagenode
|
// simulate piece stored with storagenode
|
||||||
@ -230,15 +232,21 @@ func TestRetrieve(t *testing.T) {
|
|||||||
for totalAllocated < tt.respSize {
|
for totalAllocated < tt.respSize {
|
||||||
// Send bandwidth bandwidthAllocation
|
// Send bandwidth bandwidthAllocation
|
||||||
totalAllocated += tt.allocSize
|
totalAllocated += tt.allocSize
|
||||||
|
|
||||||
|
ba := pb.RenterBandwidthAllocation{
|
||||||
|
Data: serializeData(&pb.RenterBandwidthAllocation_Data{
|
||||||
|
PayerAllocation: &pb.PayerBandwidthAllocation{},
|
||||||
|
Total: totalAllocated,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := cryptopasta.Sign(ba.Data, TS.k.(*ecdsa.PrivateKey))
|
||||||
|
assert.NoError(err)
|
||||||
|
ba.Signature = s
|
||||||
|
|
||||||
err = stream.Send(
|
err = stream.Send(
|
||||||
&pb.PieceRetrieval{
|
&pb.PieceRetrieval{
|
||||||
Bandwidthallocation: &pb.RenterBandwidthAllocation{
|
Bandwidthallocation: &ba,
|
||||||
Signature: []byte{'A', 'B'},
|
|
||||||
Data: serializeData(&pb.RenterBandwidthAllocation_Data{
|
|
||||||
PayerAllocation: &pb.PayerBandwidthAllocation{},
|
|
||||||
Total: totalAllocated,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
@ -254,8 +262,8 @@ func TestRetrieve(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data = fmt.Sprintf("%s%s", data, string(resp.Content))
|
data = fmt.Sprintf("%s%s", data, string(resp.GetContent()))
|
||||||
totalRetrieved += resp.Size
|
totalRetrieved += resp.GetSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
@ -269,7 +277,7 @@ func TestRetrieve(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestStore(t *testing.T) {
|
func TestStore(t *testing.T) {
|
||||||
TS := NewTestServer()
|
TS := NewTestServer(t)
|
||||||
defer TS.Stop()
|
defer TS.Stop()
|
||||||
|
|
||||||
db := TS.s.DB.DB
|
db := TS.s.DB.DB
|
||||||
@ -322,7 +330,6 @@ func TestStore(t *testing.T) {
|
|||||||
msg := &pb.PieceStore{
|
msg := &pb.PieceStore{
|
||||||
Piecedata: &pb.PieceStore_PieceData{Content: tt.content},
|
Piecedata: &pb.PieceStore_PieceData{Content: tt.content},
|
||||||
Bandwidthallocation: &pb.RenterBandwidthAllocation{
|
Bandwidthallocation: &pb.RenterBandwidthAllocation{
|
||||||
Signature: []byte{'A', 'B'},
|
|
||||||
Data: serializeData(&pb.RenterBandwidthAllocation_Data{
|
Data: serializeData(&pb.RenterBandwidthAllocation_Data{
|
||||||
PayerAllocation: &pb.PayerBandwidthAllocation{},
|
PayerAllocation: &pb.PayerBandwidthAllocation{},
|
||||||
Total: int64(len(tt.content)),
|
Total: int64(len(tt.content)),
|
||||||
@ -330,9 +337,12 @@ func TestStore(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the buffer to the stream we opened earlier
|
s, err := cryptopasta.Sign(msg.Bandwidthallocation.Data, TS.k.(*ecdsa.PrivateKey))
|
||||||
err = stream.Send(msg)
|
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
|
msg.Bandwidthallocation.Signature = s
|
||||||
|
|
||||||
|
// Write the buffer to the stream we opened earlier
|
||||||
|
stream.Send(msg)
|
||||||
|
|
||||||
resp, err := stream.CloseAndRecv()
|
resp, err := stream.CloseAndRecv()
|
||||||
if tt.err != "" {
|
if tt.err != "" {
|
||||||
@ -378,7 +388,7 @@ func TestStore(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
func TestDelete(t *testing.T) {
|
||||||
TS := NewTestServer()
|
TS := NewTestServer(t)
|
||||||
defer TS.Stop()
|
defer TS.Stop()
|
||||||
|
|
||||||
db := TS.s.DB.DB
|
db := TS.s.DB.DB
|
||||||
@ -463,8 +473,8 @@ func newTestServerStruct() *Server {
|
|||||||
return &Server{DataDir: tempDir, DB: psDB}
|
return &Server{DataDir: tempDir, DB: psDB}
|
||||||
}
|
}
|
||||||
|
|
||||||
func connect(addr string) (pb.PieceStoreRoutesClient, *grpc.ClientConn) {
|
func connect(addr string, o ...grpc.DialOption) (pb.PieceStoreRoutesClient, *grpc.ClientConn) {
|
||||||
conn, err := grpc.Dial(addr, grpc.WithInsecure())
|
conn, err := grpc.Dial(addr, o...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("did not connect: %v", err)
|
log.Fatalf("did not connect: %v", err)
|
||||||
}
|
}
|
||||||
@ -479,15 +489,38 @@ type TestServer struct {
|
|||||||
grpcs *grpc.Server
|
grpcs *grpc.Server
|
||||||
conn *grpc.ClientConn
|
conn *grpc.ClientConn
|
||||||
c pb.PieceStoreRoutesClient
|
c pb.PieceStoreRoutesClient
|
||||||
|
k crypto.PrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTestServer() *TestServer {
|
func NewTestServer(t *testing.T) *TestServer {
|
||||||
s := newTestServerStruct()
|
check := func(e error) {
|
||||||
grpcs := grpc.NewServer()
|
if !assert.NoError(t, e) {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ts := &TestServer{s: s, grpcs: grpcs}
|
caS, err := provider.NewCA(context.Background(), 12, 4)
|
||||||
|
check(err)
|
||||||
|
fiS, err := caS.NewIdentity()
|
||||||
|
check(err)
|
||||||
|
so, err := fiS.ServerOption()
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
caC, err := provider.NewCA(context.Background(), 12, 4)
|
||||||
|
check(err)
|
||||||
|
fiC, err := caC.NewIdentity()
|
||||||
|
check(err)
|
||||||
|
co, err := fiC.DialOption()
|
||||||
|
check(err)
|
||||||
|
|
||||||
|
s := newTestServerStruct()
|
||||||
|
grpcs := grpc.NewServer(so)
|
||||||
|
|
||||||
|
k, ok := fiC.Key.(*ecdsa.PrivateKey)
|
||||||
|
assert.True(t, ok)
|
||||||
|
ts := &TestServer{s: s, grpcs: grpcs, k: k}
|
||||||
addr := ts.start()
|
addr := ts.start()
|
||||||
ts.c, ts.conn = connect(addr)
|
ts.c, ts.conn = connect(addr, co)
|
||||||
|
|
||||||
return ts
|
return ts
|
||||||
}
|
}
|
||||||
|
@ -65,8 +65,8 @@ func (s *Server) storeData(ctx context.Context, stream pb.PieceStoreRoutes_Store
|
|||||||
// Delete data if we error
|
// Delete data if we error
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
if err = s.deleteByID(id); err != nil {
|
if deleteErr := s.deleteByID(id); deleteErr != nil {
|
||||||
log.Printf("Failed on deleteByID in Store: %s", err.Error())
|
log.Printf("Failed on deleteByID in Store: %s", deleteErr.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -82,9 +82,9 @@ func (s *Server) storeData(ctx context.Context, stream pb.PieceStoreRoutes_Store
|
|||||||
reader := NewStreamReader(s, stream)
|
reader := NewStreamReader(s, stream)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
err := s.DB.WriteBandwidthAllocToDB(reader.bandwidthAllocation)
|
baWriteErr := s.DB.WriteBandwidthAllocToDB(reader.bandwidthAllocation)
|
||||||
if err != nil {
|
if baWriteErr != nil {
|
||||||
log.Printf("WriteBandwidthAllocToDB Error: %s\n", err.Error())
|
log.Printf("WriteBandwidthAllocToDB Error: %s\n", baWriteErr.Error())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/peer"
|
||||||
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -136,8 +137,27 @@ func PeerIdentityFromCerts(leaf, ca *x509.Certificate) (*PeerIdentity, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status returns the status of the identity cert/key files for the config
|
// PeerIdentityFromContext loads a PeerIdentity from a ctx TLS credentials
|
||||||
func (is IdentitySetupConfig) Status() TLSFilesStatus {
|
func PeerIdentityFromContext(ctx context.Context) (*PeerIdentity, error) {
|
||||||
|
p, ok := peer.FromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return nil, Error.New("unable to get grpc peer from contex")
|
||||||
|
}
|
||||||
|
tlsInfo := p.AuthInfo.(credentials.TLSInfo)
|
||||||
|
c := tlsInfo.State.PeerCertificates
|
||||||
|
if len(c) < 2 {
|
||||||
|
return nil, Error.New("invalid certificate chain")
|
||||||
|
}
|
||||||
|
pi, err := PeerIdentityFromCerts(c[0], c[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pi, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat returns the status of the identity cert/key files for the config
|
||||||
|
func (is IdentitySetupConfig) Stat() TLSFilesStatus {
|
||||||
return statTLSFiles(is.CertPath, is.KeyPath)
|
return statTLSFiles(is.CertPath, is.KeyPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
|
|
||||||
"storj.io/storj/pkg/eestream"
|
"storj.io/storj/pkg/eestream"
|
||||||
"storj.io/storj/pkg/piecestore/rpc/client"
|
"storj.io/storj/pkg/piecestore/rpc/client"
|
||||||
|
"storj.io/storj/pkg/provider"
|
||||||
"storj.io/storj/pkg/ranger"
|
"storj.io/storj/pkg/ranger"
|
||||||
"storj.io/storj/pkg/transport"
|
"storj.io/storj/pkg/transport"
|
||||||
"storj.io/storj/pkg/utils"
|
"storj.io/storj/pkg/utils"
|
||||||
@ -38,7 +39,8 @@ type dialer interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type defaultDialer struct {
|
type defaultDialer struct {
|
||||||
t transport.Client
|
t transport.Client
|
||||||
|
identity *provider.FullIdentity
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *defaultDialer) dial(ctx context.Context, node *proto.Node) (ps client.PSClient, err error) {
|
func (d *defaultDialer) dial(ctx context.Context, node *proto.Node) (ps client.PSClient, err error) {
|
||||||
@ -48,7 +50,7 @@ func (d *defaultDialer) dial(ctx context.Context, node *proto.Node) (ps client.P
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return client.NewPSClient(c, 0)
|
return client.NewPSClient(c, 0, d.identity.Key)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ecClient struct {
|
type ecClient struct {
|
||||||
@ -57,8 +59,8 @@ type ecClient struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewClient from the given TransportClient and max buffer memory
|
// NewClient from the given TransportClient and max buffer memory
|
||||||
func NewClient(t transport.Client, mbm int) Client {
|
func NewClient(identity *provider.FullIdentity, t transport.Client, mbm int) Client {
|
||||||
d := defaultDialer{t: t}
|
d := defaultDialer{identity: identity, t: t}
|
||||||
return &ecClient{d: &d, mbm: mbm}
|
return &ecClient{d: &d, mbm: mbm}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ package ecclient
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -18,6 +20,7 @@ import (
|
|||||||
|
|
||||||
"storj.io/storj/pkg/eestream"
|
"storj.io/storj/pkg/eestream"
|
||||||
"storj.io/storj/pkg/piecestore/rpc/client"
|
"storj.io/storj/pkg/piecestore/rpc/client"
|
||||||
|
"storj.io/storj/pkg/provider"
|
||||||
"storj.io/storj/pkg/ranger"
|
"storj.io/storj/pkg/ranger"
|
||||||
proto "storj.io/storj/protos/overlay"
|
proto "storj.io/storj/protos/overlay"
|
||||||
)
|
)
|
||||||
@ -59,7 +62,9 @@ func TestNewECClient(t *testing.T) {
|
|||||||
tc := NewMockClient(ctrl)
|
tc := NewMockClient(ctrl)
|
||||||
mbm := 1234
|
mbm := 1234
|
||||||
|
|
||||||
ec := NewClient(tc, mbm)
|
privKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
identity := &provider.FullIdentity{Key: privKey}
|
||||||
|
ec := NewClient(identity, tc, mbm)
|
||||||
assert.NotNil(t, ec)
|
assert.NotNil(t, ec)
|
||||||
|
|
||||||
ecc, ok := ec.(*ecClient)
|
ecc, ok := ec.(*ecClient)
|
||||||
@ -78,6 +83,9 @@ func TestDefaultDialer(t *testing.T) {
|
|||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
privKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
identity := &provider.FullIdentity{Key: privKey}
|
||||||
|
|
||||||
for i, tt := range []struct {
|
for i, tt := range []struct {
|
||||||
err error
|
err error
|
||||||
errString string
|
errString string
|
||||||
@ -90,7 +98,7 @@ func TestDefaultDialer(t *testing.T) {
|
|||||||
tc := NewMockClient(ctrl)
|
tc := NewMockClient(ctrl)
|
||||||
tc.EXPECT().DialNode(gomock.Any(), node0).Return(nil, tt.err)
|
tc.EXPECT().DialNode(gomock.Any(), node0).Return(nil, tt.err)
|
||||||
|
|
||||||
dd := defaultDialer{t: tc}
|
dd := defaultDialer{t: tc, identity: identity}
|
||||||
_, err := dd.dial(ctx, node0)
|
_, err := dd.dial(ctx, node0)
|
||||||
|
|
||||||
if tt.errString != "" {
|
if tt.errString != "" {
|
||||||
|
Loading…
Reference in New Issue
Block a user