Skip to content

Commit

Permalink
go/registry: Use signature.MultiSigned for node descriptors
Browse files Browse the repository at this point in the history
To ensure that nodes demonstrate proof that they posess the private keys
for all public keys contained in their descriptor, node descriptors now
must be signed by the node, consensus, p2p and TLS certificate key.

Note: Node descriptors generated prior to this change are now invalid and
will be rejected.
  • Loading branch information
Yawning committed Jan 27, 2020
1 parent 40fec19 commit 2e7209b
Show file tree
Hide file tree
Showing 16 changed files with 482 additions and 264 deletions.
8 changes: 8 additions & 0 deletions .changelog/2599.breaking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Ensure that node descriptors are signed by all public keys

To ensure that nodes demonstrate proof that they posess the private keys
for all public keys contained in their descriptor, node descriptors now
must be signed by the node, consensus, p2p and TLS certificate key.

Note: Node descriptors generated prior to this change are now invalid and
will be rejected.
30 changes: 15 additions & 15 deletions go/common/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ var (

teeHashContext = []byte("oasis-core/node: TEE RAK binding")

_ prettyprint.PrettyPrinter = (*SignedNode)(nil)
_ prettyprint.PrettyPrinter = (*MultiSignedNode)(nil)
)

// Node represents public connectivity information about an Oasis node.
Expand Down Expand Up @@ -309,37 +309,37 @@ func (n *Node) String() string {
return "<Node id=" + n.ID.String() + ">"
}

// SignedNode is a signed blob containing a CBOR-serialized Node.
type SignedNode struct {
signature.Signed
// MultiSignedNode is a multi-signed blob containing a CBOR-serialized Node.
type MultiSignedNode struct {
signature.MultiSigned
}

// Open first verifies the blob signature and then unmarshals the blob.
func (s *SignedNode) Open(context signature.Context, node *Node) error { // nolint: interfacer
return s.Signed.Open(context, node)
// Open first verifies the blob signatures and then unmarshals the blob.
func (s *MultiSignedNode) Open(context signature.Context, node *Node) error {
return s.MultiSigned.Open(context, node)
}

// PrettyPrint writes a pretty-printed representation of the type
// to the given writer.
func (s SignedNode) PrettyPrint(prefix string, w io.Writer) {
func (s MultiSignedNode) PrettyPrint(prefix string, w io.Writer) {
var n Node
if err := cbor.Unmarshal(s.Signed.Blob, &n); err != nil {
if err := cbor.Unmarshal(s.MultiSigned.Blob, &n); err != nil {
fmt.Fprintf(w, "%s<malformed: %s>\n", prefix, err)
return
}

pp := signature.NewPrettySigned(s.Signed, n)
pp := signature.NewPrettyMultiSigned(s.MultiSigned, n)
pp.PrettyPrint(prefix, w)
}

// SignNode serializes the Node and signs the result.
func SignNode(signer signature.Signer, context signature.Context, node *Node) (*SignedNode, error) {
signed, err := signature.SignSigned(signer, context, node)
// MultiSignNode serializes the Node and multi-signs the result.
func MultiSignNode(signers []signature.Signer, context signature.Context, node *Node) (*MultiSignedNode, error) {
multiSigned, err := signature.SignMultiSigned(signers, context, node)
if err != nil {
return nil, err
}

return &SignedNode{
Signed: *signed,
return &MultiSignedNode{
MultiSigned: *multiSigned,
}, nil
}
6 changes: 4 additions & 2 deletions go/consensus/tendermint/apps/registry/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,10 @@ func (app *registryApplication) InitChain(ctx *abci.Context, request types.Reque
}
}
for _, v := range st.Nodes {
// The node signer isn't guaranteed to be the owner, and in most cases
// will just be the node self signing.
ctx.Logger().Debug("InitChain: Registering genesis node",
"node_owner", v.Signature.PublicKey,
"node_signer", v.Signatures[0].PublicKey,
)
if err := app.registerNode(ctx, state, v); err != nil {
ctx.Logger().Error("InitChain: failed to register node",
Expand Down Expand Up @@ -149,7 +151,7 @@ func (rq *registryQuerier) Genesis(ctx context.Context) (*registry.Genesis, erro
}

// We only want to keep the nodes that are validators.
validatorNodes := make([]*node.SignedNode, 0)
validatorNodes := make([]*node.MultiSignedNode, 0)
for _, sn := range signedNodes {
var n node.Node
if err = cbor.Unmarshal(sn.Blob, &n); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion go/consensus/tendermint/apps/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (app *registryApplication) ExecuteTx(ctx *abci.Context, tx *transaction.Tra
case registry.MethodDeregisterEntity:
return app.deregisterEntity(ctx, state)
case registry.MethodRegisterNode:
var sigNode node.SignedNode
var sigNode node.MultiSignedNode
if err := cbor.Unmarshal(tx.Body, &sigNode); err != nil {
return err
}
Expand Down
12 changes: 6 additions & 6 deletions go/consensus/tendermint/apps/registry/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ func (s *ImmutableState) Node(id signature.PublicKey) (*node.Node, error) {
return nil, registry.ErrNoSuchNode
}

var signedNode node.SignedNode
var signedNode node.MultiSignedNode
if err = cbor.Unmarshal(signedNodeRaw, &signedNode); err != nil {
return nil, err
}
Expand Down Expand Up @@ -199,7 +199,7 @@ func (s *ImmutableState) Nodes() ([]*node.Node, error) {
return true
}

var signedNode node.SignedNode
var signedNode node.MultiSignedNode
if err := cbor.Unmarshal(value, &signedNode); err != nil {
panic("tendermint/registry: corrupted state: " + err.Error())
}
Expand All @@ -217,8 +217,8 @@ func (s *ImmutableState) Nodes() ([]*node.Node, error) {
return nodes, nil
}

func (s *ImmutableState) SignedNodes() ([]*node.SignedNode, error) {
var nodes []*node.SignedNode
func (s *ImmutableState) SignedNodes() ([]*node.MultiSignedNode, error) {
var nodes []*node.MultiSignedNode
s.Snapshot.IterateRange(
signedNodeKeyFmt.Encode(),
nil,
Expand All @@ -228,7 +228,7 @@ func (s *ImmutableState) SignedNodes() ([]*node.SignedNode, error) {
return true
}

var signedNode node.SignedNode
var signedNode node.MultiSignedNode
if err := cbor.Unmarshal(value, &signedNode); err != nil {
panic("tendermint/registry: corrupted state: " + err.Error())
}
Expand Down Expand Up @@ -551,7 +551,7 @@ func (s *MutableState) RemoveEntity(id signature.PublicKey) (*entity.Entity, err
return nil, registry.ErrNoSuchEntity
}

func (s *MutableState) SetNode(node *node.Node, signedNode *node.SignedNode) error {
func (s *MutableState) SetNode(node *node.Node, signedNode *node.MultiSignedNode) error {
// Ensure that the entity exists.
ent, err := s.getSignedEntityRaw(node.EntityID)
if ent == nil || err != nil {
Expand Down
18 changes: 13 additions & 5 deletions go/consensus/tendermint/apps/registry/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func (app *registryApplication) deregisterEntity(ctx *abci.Context, state *regis
func (app *registryApplication) registerNode( // nolint: gocyclo
ctx *abci.Context,
state *registryState.MutableState,
sigNode *node.SignedNode,
sigNode *node.MultiSignedNode,
) error {
if ctx.IsCheckOnly() {
return nil
Expand Down Expand Up @@ -182,17 +182,25 @@ func (app *registryApplication) registerNode( // nolint: gocyclo

// Charge gas for node registration if signed by entity. For node-signed
// registrations, the gas charges are pre-paid by the entity.
if sigNode.Signature.PublicKey.Equal(newNode.EntityID) {
isEntitySigned := sigNode.MultiSigned.IsSignedBy(newNode.EntityID)
if isEntitySigned {
if err = ctx.Gas().UseGas(1, registry.GasOpRegisterNode, params.GasCosts); err != nil {
return err
}
}

// Make sure the signer of the transaction matches the signer of the node.
// Make sure the signer of the transaction is the node identity key
// or the entity (iff the registration is entity signed).
// NOTE: If this is invoked during InitChain then there is no actual transaction
// and thus no transaction signer so we must skip this check.
if !ctx.IsInitChain() && !sigNode.Signature.PublicKey.Equal(ctx.TxSigner()) {
return registry.ErrIncorrectTxSigner
if !ctx.IsInitChain() {
expectedTxSigner := newNode.ID
if isEntitySigned {
expectedTxSigner = newNode.EntityID
}
if !ctx.TxSigner().Equal(expectedTxSigner) {
return registry.ErrIncorrectTxSigner
}
}

// Check runtime's whitelist.
Expand Down
Loading

0 comments on commit 2e7209b

Please sign in to comment.