Skip to content
This repository has been archived by the owner on Jan 3, 2023. It is now read-only.

Mir crypto #208

Merged
merged 18 commits into from
Jun 21, 2022
Merged
5 changes: 4 additions & 1 deletion api/api_net.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"context"
"time"

metrics "github.com/libp2p/go-libp2p-core/metrics"
"github.com/libp2p/go-libp2p-core/metrics"
"github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/libp2p/go-libp2p-core/protocol"
Expand Down Expand Up @@ -64,6 +64,9 @@ type Net interface {

// ID returns peerID of libp2p node backing this API
ID(context.Context) (peer.ID, error) //perm:read

// NetSign signs a message using the private key of the node with ID.
NetSign(ctx context.Context, id peer.ID, msg []byte) ([]byte, error)
}

type CommonNet interface {
Expand Down
15 changes: 15 additions & 0 deletions api/mocks/mock_full.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 14 additions & 1 deletion api/proxy_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions api/v0api/v0mocks/mock_full.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 43 additions & 3 deletions chain/consensus/mir/app.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,63 @@
package mir

import (
"fmt"

"github.com/filecoin-project/mir/pkg/events"
"github.com/filecoin-project/mir/pkg/modules"
"github.com/filecoin-project/mir/pkg/pb/eventpb"
"github.com/filecoin-project/mir/pkg/pb/requestpb"
"github.com/filecoin-project/mir/pkg/reqstore"
t "github.com/filecoin-project/mir/pkg/types"
)

type Tx []byte

type Application struct {
reqStore modules.RequestStore
reqStore *reqstore.VolatileRequestStore
ChainNotify chan []Tx
}

func NewApplication(reqStore modules.RequestStore) *Application {
func NewApplication(reqStore *reqstore.VolatileRequestStore) *Application {
app := Application{
reqStore: reqStore,
ChainNotify: make(chan []Tx),
}
return &app
}

func (app *Application) Apply(batch *requestpb.Batch) error {
func (app *Application) ApplyEvents(eventsIn *events.EventList) (*events.EventList, error) {
return modules.ApplyEventsSequentially(eventsIn, app.ApplyEvent)
}

func (app *Application) ApplyEvent(event *eventpb.Event) (*events.EventList, error) {
switch e := event.Type.(type) {
case *eventpb.Event_Deliver:
if err := app.ApplyBatch(e.Deliver.Batch); err != nil {
return nil, fmt.Errorf("app batch delivery error: %w", err)
}
case *eventpb.Event_AppSnapshotRequest:
data, err := app.Snapshot()
if err != nil {
return nil, fmt.Errorf("app snapshot error: %w", err)
}
return (&events.EventList{}).PushBack(events.AppSnapshot(
t.ModuleID(e.AppSnapshotRequest.Module),
t.EpochNr(e.AppSnapshotRequest.Epoch),
data,
)), nil
case *eventpb.Event_AppRestoreState:
if err := app.RestoreState(e.AppRestoreState.Data); err != nil {
return nil, fmt.Errorf("app restore state error: %w", err)
}
default:
return nil, fmt.Errorf("unexpected type of App event: %T", event.Type)
}

return &events.EventList{}, nil
}

func (app *Application) ApplyBatch(batch *requestpb.Batch) error {
var block []Tx

for _, reqRef := range batch.Requests {
Expand Down Expand Up @@ -51,3 +88,6 @@ func (app *Application) Snapshot() ([]byte, error) {
func (app *Application) RestoreState(snapshot []byte) error {
return nil
}

// The ImplementsModule method only serves the purpose of indicating that this is a Module and must not be called.
func (app *Application) ImplementsModule() {}
112 changes: 112 additions & 0 deletions chain/consensus/mir/crypto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package mir

import (
"context"
"crypto/sha256"
"strings"

libp2pcrypto "github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/peer"
"golang.org/x/xerrors"

"github.com/filecoin-project/go-address"
filcrypto "github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/lib/sigs"
mircrypto "github.com/filecoin-project/mir/pkg/crypto"
t "github.com/filecoin-project/mir/pkg/types"
)

var _ mircrypto.Impl = &CryptoManager{}

type NodeCrypto interface {
Sign(ctx context.Context, id peer.ID, msg []byte) ([]byte, error)
}

// CryptoManager implements Mir's crypto interface.
// It uses the node's libp2p private key to sign Mir messages.
// libp2p public keys and IDs correspond to each other.
// Because of that we don't need trust establishment mechanisms like CA.
// We ED25519 signing algorithm, because Filecoin uses it.
matejpavlovic marked this conversation as resolved.
Show resolved Hide resolved
// At present, libp2p doesn't support BLS. If we want to switch to BLS in the future,
// we will need to reimplement CryptoManager.
// Probably we will need to introduce our own consensus module keys and establish trust somehow.
type CryptoManager struct {
id peer.ID
api NodeCrypto
}

func NewCryptoManager(id peer.ID, api NodeCrypto) (*CryptoManager, error) {
pub, err := id.ExtractPublicKey()
if err != nil {
return nil, xerrors.Errorf("invalid peer ID: ", err)
}
if pub.Type() != libp2pcrypto.Ed25519 {
return nil, xerrors.New("must be ED25519 public key")
}

return &CryptoManager{id, api}, nil
}

// Sign signs the provided data and returns the resulting signature.
// The data to be signed is the concatenation of all the passed byte slices.
// A signature produced by Sign is verifiable using VerifyNodeSig or VerifyClientSig,
// if, respectively, RegisterNodeKey or RegisterClientKey has been invoked with the corresponding public key.
// Note that the private key used to produce the signature cannot be set ("registered") through this interface.
// Storing and using the private key is completely implementation-dependent.
func (c *CryptoManager) Sign(data [][]byte) (bytes []byte, err error) {
return c.api.Sign(context.Background(), c.id, hash(data))

}

// VerifyNodeSig verifies a signature produced by the node with numeric ID nodeID over data.
// Returns nil on success (i.e., if the given signature is valid) and a non-nil error otherwise.
// Note that RegisterNodeKey must be used to register the node's public key before calling VerifyNodeSig,
// otherwise VerifyNodeSig will fail.
func (c *CryptoManager) VerifyNodeSig(data [][]byte, signature []byte, nodeID t.NodeID) error {
node, err := getID(nodeID.Pb())
if err != nil {
return err
}
return c.verifySig(data, signature, node)
}

// VerifyClientSig verifies a signature produced by the client with numeric ID clientID over data.
// Returns nil on success (i.e., if the given signature is valid) and a non-nil error otherwise.
// Note that RegisterClientKey must be used to register the client's public key before calling VerifyClientSig,
// otherwise VerifyClientSig will fail.
func (c *CryptoManager) VerifyClientSig(data [][]byte, signature []byte, clientID t.ClientID) error {
client, err := getID(clientID.Pb())
if err != nil {
return err
}
return c.verifySig(data, signature, client)
}

func (c *CryptoManager) verifySig(data [][]byte, signature []byte, addr address.Address) error {
var sig filcrypto.Signature
if err := sig.UnmarshalBinary(signature); err != nil {
return err
}

return sigs.Verify(&sig, addr, hash(data))
}

func hash(data [][]byte) []byte {
h := sha256.New()
for _, d := range data {
h.Write(d)
}
return h.Sum(nil)
}

func getID(nodeID string) (address.Address, error) {
addrParts := strings.Split(nodeID, ":")
if len(addrParts) != 2 {
return address.Undef, xerrors.Errorf("invalid node ID: %s", nodeID)
dnkolegov marked this conversation as resolved.
Show resolved Hide resolved
}
nodeAddr, err := address.NewFromString(addrParts[1])
if err != nil {
return address.Undef, err
}
return nodeAddr, nil
}
Loading