Skip to content

Commit

Permalink
Merge pull request #1994 from oasislabs/kostko/feature/consensus-tx-c…
Browse files Browse the repository at this point in the history
…hecks

Tighten consensus transaction validation
  • Loading branch information
kostko authored Aug 20, 2019
2 parents 5931711 + 4c642ba commit 9d6955c
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 59 deletions.
59 changes: 34 additions & 25 deletions go/common/identity/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,14 @@ func doLoadOrGenerate(dataDir string, signerFactory signature.SignerFactory, sho
return nil, err
}

tlsCert, err = generateTLSCert(dataDir)
tlsCert, err = GenerateTLSCert()
if err != nil {
return nil, err
}

if err = saveTLSCert(dataDir, tlsCert); err != nil {
return nil, err
}
}

return &Identity{
Expand Down Expand Up @@ -163,30 +167,14 @@ func loadTLSCert(dataDir string) (*tls.Certificate, error) {
}, nil
}

func generateTLSCert(dataDir string) (*tls.Certificate, error) {
tlsKeyPath, tlsCertPath := tlsCertPaths(dataDir)

// GenerateTLSCert generates a node TLS certificate.
func GenerateTLSCert() (*tls.Certificate, error) {
// Generate a new X509 key pair.
tlsKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}

// Persist key pair.
der, err := x509.MarshalECPrivateKey(tlsKey)
if err != nil {
return nil, err
}

tlsKeyPEM := pem.EncodeToMemory(&pem.Block{
Type: tlsKeyPEMType,
Bytes: der,
})

if err = ioutil.WriteFile(tlsKeyPath, tlsKeyPEM, 0600); err != nil {
return nil, err
}

// Generate X509 certificate based on the key pair.
certTemplate := tlsTemplate
// Valid since one hour before issue.
Expand All @@ -199,18 +187,39 @@ func generateTLSCert(dataDir string) (*tls.Certificate, error) {
return nil, err
}

return &tls.Certificate{
Certificate: [][]byte{tlsCertDer},
PrivateKey: tlsKey,
}, nil
}

func saveTLSCert(dataDir string, cert *tls.Certificate) error {
tlsKeyPath, tlsCertPath := tlsCertPaths(dataDir)

// Persist key pair.
der, err := x509.MarshalECPrivateKey(cert.PrivateKey.(*ecdsa.PrivateKey))
if err != nil {
return err
}

tlsKeyPEM := pem.EncodeToMemory(&pem.Block{
Type: tlsKeyPEMType,
Bytes: der,
})

if err = ioutil.WriteFile(tlsKeyPath, tlsKeyPEM, 0600); err != nil {
return err
}

// Persist TLS certificate.
tlsCertPEM := pem.EncodeToMemory(&pem.Block{
Type: tlsCertPEMType,
Bytes: tlsCertDer,
Bytes: cert.Certificate[0],
})

if err = ioutil.WriteFile(tlsCertPath, tlsCertPEM, 0644); err != nil {
return nil, err
return err
}

return &tls.Certificate{
Certificate: [][]byte{tlsCertDer},
PrivateKey: tlsKey,
}, nil
return nil
}
12 changes: 11 additions & 1 deletion go/common/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ const (
RoleMergeWorker RolesMask = 1 << 4
// RoleValidator is the Ekiden validator role.
RoleValidator RolesMask = 1 << 5

// RoleReserved are all the bits of the Eiden Node roles bitmask
// that are reserved and must not be used.
RoleReserved RolesMask = ((1 << 32) - 1) & ^((RoleValidator << 1) - 1)
)

// AddRoles adds the Node roles
Expand Down Expand Up @@ -279,8 +283,14 @@ type TEEHardware uint8

// TEE Hardware implementations.
const (
TEEHardwareInvalid TEEHardware = 0
// TEEHardwareInvalid is a non-TEE implementation.
TEEHardwareInvalid TEEHardware = 0
// TEEHardwareIntelSGX is an Intel SGX TEE implementation.
TEEHardwareIntelSGX TEEHardware = 1

// TEEHardwareReserved is the first reserved hardware implementation
// identifier. All equal or greater identifiers are reserved.
TEEHardwareReserved TEEHardware = TEEHardwareIntelSGX + 1
)

// FromProto deserializes a protobuf into a TEEHardware.
Expand Down
80 changes: 77 additions & 3 deletions go/registry/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,6 @@ func (t *Timestamp) UnmarshalCBOR(data []byte) error {

// VerifyRegisterEntityArgs verifies arguments for RegisterEntity.
func VerifyRegisterEntityArgs(logger *logging.Logger, sigEnt *entity.SignedEntity, isGenesis bool) (*entity.Entity, error) {
// XXX: Ensure ent is well-formed.
var ent entity.Entity
if sigEnt == nil {
return nil, ErrInvalidArgument
Expand Down Expand Up @@ -265,7 +264,6 @@ func VerifyDeregisterEntityArgs(logger *logging.Logger, sigTimestamp *signature.

// VerifyRegisterNodeArgs verifies arguments for RegisterNode.
func VerifyRegisterNodeArgs(logger *logging.Logger, sigNode *node.SignedNode, entity *entity.Entity, now time.Time, isGenesis bool) (*node.Node, error) {
// XXX: Ensure node is well-formed.
var n node.Node
if sigNode == nil {
return nil, ErrInvalidArgument
Expand Down Expand Up @@ -327,6 +325,20 @@ func VerifyRegisterNodeArgs(logger *logging.Logger, sigNode *node.SignedNode, en
return nil, ErrInvalidArgument
}

// Make sure that a node has at least one valid role.
switch {
case n.Roles == 0:
logger.Error("RegisterNode: no roles specified",
"node", n,
)
return nil, ErrInvalidArgument
case n.HasRoles(node.RoleReserved):
logger.Error("RegisterNode: invalid role specified",
"node", n,
)
return nil, ErrInvalidArgument
}

// TODO: Key manager nodes maybe should be restricted to only being a
// key manager at the expense of breaking some of our test configs.
needRuntimes := n.HasRoles(node.RoleComputeWorker | node.RoleKeyManager) // XXX: RoleTransactionSceduler?
Expand Down Expand Up @@ -371,6 +383,37 @@ func VerifyRegisterNodeArgs(logger *logging.Logger, sigNode *node.SignedNode, en
}
}

// If node is a validator, ensure it has ConensusInfo.
if n.HasRoles(node.RoleValidator) {
// Verify that addresses are non-empty.
if len(n.Consensus.Addresses) == 0 {
logger.Error("RegisterNode: missing consensus addresses",
"node", n,
)
return nil, ErrInvalidArgument
}
}

// If node is a worker, ensure it has CommitteeInfo and P2PInfo.
if n.HasRoles(node.RoleComputeWorker | node.RoleStorageWorker | node.RoleTransactionScheduler | node.RoleKeyManager | node.RoleMergeWorker) {
// Verify that addresses are non-empty.
if len(n.Committee.Addresses) == 0 || len(n.P2P.Addresses) == 0 {
logger.Error("RegisterNode: missing committee or p2p addresses",
"node", n,
)
return nil, ErrInvalidArgument
}

// Verify that certificate is well-formed.
if _, err := n.Committee.ParseCertificate(); err != nil {
logger.Error("RegisterNode: invalid committee TLS certificate",
"node", n,
"err", err,
)
return nil, ErrInvalidArgument
}
}

return &n, nil
}

Expand Down Expand Up @@ -458,7 +501,6 @@ func VerifyNodeUpdate(logger *logging.Logger, currentNode *node.Node, newNode *n

// VerifyRegisterRuntimeArgs verifies arguments for RegisterRuntime.
func VerifyRegisterRuntimeArgs(logger *logging.Logger, sigRt *SignedRuntime, isGenesis bool) (*Runtime, error) {
// XXX: Ensure runtime is well-formed.
var rt Runtime
if sigRt == nil {
return nil, ErrInvalidArgument
Expand All @@ -485,6 +527,22 @@ func VerifyRegisterRuntimeArgs(logger *logging.Logger, sigRt *SignedRuntime, isG
if rt.ID.Equal(rt.KeyManager) {
return nil, ErrInvalidArgument
}

// Ensure there is at least one member of the transaction scheduler group.
if rt.TransactionSchedulerGroupSize == 0 {
logger.Error("RegisterRuntime: transaction scheduler group too small",
"runtime", rt,
)
return nil, ErrInvalidArgument
}

// Ensure there is at least one member of the storage group.
if rt.StorageGroupSize == 0 {
logger.Error("RegisterRuntime: storage group too small",
"runtime", rt,
)
return nil, ErrInvalidArgument
}
case KindKeyManager:
if !rt.ID.Equal(rt.KeyManager) {
return nil, ErrInvalidArgument
Expand All @@ -498,6 +556,22 @@ func VerifyRegisterRuntimeArgs(logger *logging.Logger, sigRt *SignedRuntime, isG
return nil, ErrInvalidArgument
}

// Ensure there is at least one member of the replication group.
if rt.ReplicaGroupSize == 0 {
logger.Error("RegisterRuntime: replication group too small",
"runtime", rt,
)
return nil, ErrInvalidArgument
}

// Ensure a valid TEE hardware is specified.
if rt.TEEHardware >= node.TEEHardwareReserved {
logger.Error("RegisterRuntime: invalid TEE hardware specified",
"runtime", rt,
)
return nil, ErrInvalidArgument
}

return &rt, nil
}

Expand Down
90 changes: 82 additions & 8 deletions go/registry/tests/tester.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/oasislabs/ekiden/go/common/crypto/signature"
memorySigner "github.com/oasislabs/ekiden/go/common/crypto/signature/signers/memory"
"github.com/oasislabs/ekiden/go/common/entity"
"github.com/oasislabs/ekiden/go/common/identity"
"github.com/oasislabs/ekiden/go/common/node"
epochtime "github.com/oasislabs/ekiden/go/epochtime/api"
epochtimeTests "github.com/oasislabs/ekiden/go/epochtime/tests"
Expand Down Expand Up @@ -116,7 +117,22 @@ func testRegistryEntityNodes(t *testing.T, backend api.Backend, timeSource epoch

for _, vec := range nodes {
for _, v := range vec {
err := backend.RegisterNode(context.Background(), v.SignedRegistration)
err := backend.RegisterNode(context.Background(), v.SignedInvalidRegistration1)
require.Error(err, "register committee node without P2P addresses")

err = backend.RegisterNode(context.Background(), v.SignedInvalidRegistration2)
require.Error(err, "register committee node without committee addresses")

err = backend.RegisterNode(context.Background(), v.SignedInvalidRegistration3)
require.Error(err, "register committee node without committee certificate")

err = backend.RegisterNode(context.Background(), v.SignedInvalidRegistration4)
require.Error(err, "register node without roles")

err = backend.RegisterNode(context.Background(), v.SignedInvalidRegistration5)
require.Error(err, "register node with reserved roles")

err = backend.RegisterNode(context.Background(), v.SignedRegistration)
require.NoError(err, "RegisterNode")

select {
Expand Down Expand Up @@ -361,6 +377,11 @@ type TestNode struct {
Signer signature.Signer

SignedRegistration *node.SignedNode
SignedInvalidRegistration1 *node.SignedNode
SignedInvalidRegistration2 *node.SignedNode
SignedInvalidRegistration3 *node.SignedNode
SignedInvalidRegistration4 *node.SignedNode
SignedInvalidRegistration5 *node.SignedNode
SignedValidReRegistration *node.SignedNode
SignedInvalidReRegistration *node.SignedNode
}
Expand Down Expand Up @@ -413,15 +434,66 @@ func (ent *TestEntity) NewTestNodes(nCompute int, nStorage int, runtimes []*Test
Port: 451,
},
}
nod.Node.P2P.Addresses = append(nod.Node.P2P.Addresses, addr)
nod.Node.Committee.Addresses = append(nod.Node.Committee.Addresses, addr)
// Generate dummy TLS certificate.
tlsCert, err := identity.GenerateTLSCert()
if err != nil {
return nil, err
}
nod.Node.Committee.Certificate = tlsCert.Certificate[0]

nod.SignedRegistration, err = node.SignNode(ent.Signer, api.RegisterNodeSignatureContext, nod.Node)
if err != nil {
return nil, err
}

// Add a registration with no P2P addresses.
invalid1 := *nod.Node
invalid1.P2P.Addresses = nil

nod.SignedInvalidRegistration1, err = node.SignNode(ent.Signer, api.RegisterNodeSignatureContext, &invalid1)
if err != nil {
return nil, err
}

// Add a registration with no committee addresses.
invalid2 := *nod.Node
invalid2.Committee.Addresses = nil

nod.SignedInvalidRegistration2, err = node.SignNode(ent.Signer, api.RegisterNodeSignatureContext, &invalid2)
if err != nil {
return nil, err
}

// Add a registration with no committee certificate.
invalid3 := *nod.Node
invalid3.Committee.Certificate = nil

nod.SignedInvalidRegistration3, err = node.SignNode(ent.Signer, api.RegisterNodeSignatureContext, &invalid3)
if err != nil {
return nil, err
}

// Add a registration without any roles.
invalid4 := *nod.Node
invalid4.Roles = 0

nod.SignedInvalidRegistration4, err = node.SignNode(ent.Signer, api.RegisterNodeSignatureContext, &invalid4)
if err != nil {
return nil, err
}

// Add a registration with reserved roles.
invalid5 := *nod.Node
invalid5.Roles = 0xFFFFFFFF

signed, err := signature.SignSigned(ent.Signer, api.RegisterNodeSignatureContext, nod.Node)
nod.SignedInvalidRegistration5, err = node.SignNode(ent.Signer, api.RegisterNodeSignatureContext, &invalid5)
if err != nil {
return nil, err
}
nod.SignedRegistration = &node.SignedNode{Signed: *signed}

// Add another Re-Registration with differnet address field.
// Add another Re-Registration with different address field.
nod.UpdatedNode = &node.Node{
ID: nod.Signer.Public(),
EntityID: ent.Entity.ID,
Expand All @@ -436,12 +508,13 @@ func (ent *TestEntity) NewTestNodes(nCompute int, nStorage int, runtimes []*Test
Port: 452,
},
}
nod.UpdatedNode.P2P.Addresses = append(nod.UpdatedNode.P2P.Addresses, addr)
nod.UpdatedNode.Committee.Addresses = append(nod.UpdatedNode.Committee.Addresses, addr)
signed, err = signature.SignSigned(ent.Signer, api.RegisterNodeSignatureContext, nod.UpdatedNode)
nod.UpdatedNode.Committee.Certificate = nod.Node.Committee.Certificate
nod.SignedValidReRegistration, err = node.SignNode(ent.Signer, api.RegisterNodeSignatureContext, nod.UpdatedNode)
if err != nil {
return nil, err
}
nod.SignedValidReRegistration = &node.SignedNode{Signed: *signed}

// Add invalid Re-Registration with changed Roles field.
testRuntimeSigner := memorySigner.NewTestSigner("invalod-registration-runtime-seed")
Expand All @@ -452,12 +525,13 @@ func (ent *TestEntity) NewTestNodes(nCompute int, nStorage int, runtimes []*Test
RegistrationTime: uint64(time.Now().Unix()),
Runtimes: append(nodeRts, &node.Runtime{ID: testRuntimeSigner.Public()}),
Roles: role,
P2P: nod.Node.P2P,
Committee: nod.Node.Committee,
}
signed, err = signature.SignSigned(ent.Signer, api.RegisterNodeSignatureContext, newNode)
nod.SignedInvalidReRegistration, err = node.SignNode(ent.Signer, api.RegisterNodeSignatureContext, newNode)
if err != nil {
return nil, err
}
nod.SignedInvalidReRegistration = &node.SignedNode{Signed: *signed}

nodes = append(nodes, &nod)
}
Expand Down
Loading

0 comments on commit 9d6955c

Please sign in to comment.