diff --git a/.changelog/2929.bugfix.md b/.changelog/2929.bugfix.md new file mode 100644 index 00000000000..39afd2d99ea --- /dev/null +++ b/.changelog/2929.bugfix.md @@ -0,0 +1 @@ +go/consensus: Hash user-controlled storage key elements diff --git a/go/common/crypto/signature/signature.go b/go/common/crypto/signature/signature.go index 6add8d83f86..4efb5ca456f 100644 --- a/go/common/crypto/signature/signature.go +++ b/go/common/crypto/signature/signature.go @@ -19,6 +19,7 @@ import ( "github.com/oasislabs/ed25519" "github.com/oasislabs/oasis-core/go/common/cbor" + "github.com/oasislabs/oasis-core/go/common/crypto/hash" "github.com/oasislabs/oasis-core/go/common/pem" "github.com/oasislabs/oasis-core/go/common/prettyprint" ) @@ -210,6 +211,11 @@ func (k *PublicKey) LoadPEM(fn string, signer Signer) error { return nil } +// Hash returns a cryptographic hash of the public key. +func (k PublicKey) Hash() hash.Hash { + return hash.NewFromBytes(k[:]) +} + func (k PublicKey) isBlacklisted() bool { _, isBlacklisted := blacklistedPublicKeys.Load(k) return isBlacklisted diff --git a/go/consensus/tendermint/abci/timer.go b/go/consensus/tendermint/abci/timer.go index 201adc2ecbb..076ba3f2c61 100644 --- a/go/consensus/tendermint/abci/timer.go +++ b/go/consensus/tendermint/abci/timer.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/oasislabs/oasis-core/go/common/crypto/hash" "github.com/oasislabs/oasis-core/go/common/keyformat" "github.com/oasislabs/oasis-core/go/common/logging" "github.com/oasislabs/oasis-core/go/consensus/tendermint/api" @@ -68,6 +69,9 @@ type Timer struct { func NewTimer(ctx *api.Context, app Application, kind uint8, id []byte, data []byte) *Timer { if data == nil { data = []byte{} + } else { + h := hash.NewFromBytes(data) + data = h[:] } state := &timerState{ diff --git a/go/consensus/tendermint/apps/keymanager/state/state.go b/go/consensus/tendermint/apps/keymanager/state/state.go index 2dda20df70f..add4795d3a1 100644 --- a/go/consensus/tendermint/apps/keymanager/state/state.go +++ b/go/consensus/tendermint/apps/keymanager/state/state.go @@ -15,7 +15,7 @@ var ( // statusKeyFmt is the key manager status key format. // // Value is CBOR-serialized key manager status. - statusKeyFmt = keyformat.New(0x70, &common.Namespace{}) + statusKeyFmt = keyformat.New(0x70, keyformat.H(&common.Namespace{})) ) // ImmutableState is the immutable key manager state wrapper. diff --git a/go/consensus/tendermint/apps/registry/genesis.go b/go/consensus/tendermint/apps/registry/genesis.go index 1fe3c6ea467..b8a785ad5e0 100644 --- a/go/consensus/tendermint/apps/registry/genesis.go +++ b/go/consensus/tendermint/apps/registry/genesis.go @@ -8,6 +8,7 @@ import ( "github.com/tendermint/tendermint/abci/types" "github.com/oasislabs/oasis-core/go/common/cbor" + "github.com/oasislabs/oasis-core/go/common/crypto/signature" "github.com/oasislabs/oasis-core/go/common/node" abciAPI "github.com/oasislabs/oasis-core/go/consensus/tendermint/api" registryState "github.com/oasislabs/oasis-core/go/consensus/tendermint/apps/registry/state" @@ -147,20 +148,25 @@ func (rq *registryQuerier) Genesis(ctx context.Context) (*registry.Genesis, erro // BUG: If the debonding period will apply to other nodes, // then we need to basically persist everything. validatorNodes := make([]*node.MultiSignedNode, 0) + nodeStatuses := make(map[signature.PublicKey]*registry.NodeStatus) for _, sn := range signedNodes { var n node.Node if err = cbor.Unmarshal(sn.Blob, &n); err != nil { return nil, err } - if n.HasRoles(node.RoleValidator) { - validatorNodes = append(validatorNodes, sn) + if !n.HasRoles(node.RoleValidator) { + continue } - } - nodeStatuses, err := rq.state.NodeStatuses(ctx) - if err != nil { - return nil, err + var status *registry.NodeStatus + status, err = rq.state.NodeStatus(ctx, n.ID) + if err != nil { + return nil, err + } + + validatorNodes = append(validatorNodes, sn) + nodeStatuses[n.ID] = status } params, err := rq.state.ConsensusParameters(ctx) diff --git a/go/consensus/tendermint/apps/registry/state/state.go b/go/consensus/tendermint/apps/registry/state/state.go index 68a3b605831..7696ed4b03c 100644 --- a/go/consensus/tendermint/apps/registry/state/state.go +++ b/go/consensus/tendermint/apps/registry/state/state.go @@ -23,20 +23,20 @@ var ( // signedEntityKeyFmt is the key format used for signed entities. // // Value is CBOR-serialized signed entity. - signedEntityKeyFmt = keyformat.New(0x10, &signature.PublicKey{}) + signedEntityKeyFmt = keyformat.New(0x10, keyformat.H(&signature.PublicKey{})) // signedNodeKeyFmt is the key format used for signed nodes. // // Value is CBOR-serialized signed node. - signedNodeKeyFmt = keyformat.New(0x11, &signature.PublicKey{}) + signedNodeKeyFmt = keyformat.New(0x11, keyformat.H(&signature.PublicKey{})) // signedNodeByEntityKeyFmt is the key format used for signed node by entity // index. // // Value is empty. - signedNodeByEntityKeyFmt = keyformat.New(0x12, &signature.PublicKey{}, &signature.PublicKey{}) + signedNodeByEntityKeyFmt = keyformat.New(0x12, keyformat.H(&signature.PublicKey{}), keyformat.H(&signature.PublicKey{})) // signedRuntimeKeyFmt is the key format used for signed runtimes. // // Value is CBOR-serialized signed runtime. - signedRuntimeKeyFmt = keyformat.New(0x13, &common.Namespace{}) + signedRuntimeKeyFmt = keyformat.New(0x13, keyformat.H(&common.Namespace{})) // nodeByConsAddressKeyFmt is the key format used for the consensus address to // node public key mapping. // @@ -49,25 +49,26 @@ var ( // nodeStatusKeyFmt is the key format used for node statuses. // // Value is CBOR-serialized node status. - nodeStatusKeyFmt = keyformat.New(0x15, &signature.PublicKey{}) + nodeStatusKeyFmt = keyformat.New(0x15, keyformat.H(&signature.PublicKey{})) // parametersKeyFmt is the key format used for consensus parameters. // // Value is CBOR-serialized registry.ConsensusParameters. parametersKeyFmt = keyformat.New(0x16) // keyMapKeyFmt is the key format used for key-to-node-id map. - // This stores the consensus and P2P to Node ID mappings. + // + // This stores the consensus, P2P and TLS public keys to node ID mappings. // // Value is binary signature.PublicKey (node ID). - keyMapKeyFmt = keyformat.New(0x17, &signature.PublicKey{}) + keyMapKeyFmt = keyformat.New(0x17, keyformat.H(&signature.PublicKey{})) // suspendedRuntimeKeyFmt is the key format used for suspended runtimes. // // Value is CBOR-serialized signed runtime. - suspendedRuntimeKeyFmt = keyformat.New(0x18, &common.Namespace{}) + suspendedRuntimeKeyFmt = keyformat.New(0x18, keyformat.H(&common.Namespace{})) // signedRuntimeByEntityKeyFmt is the key format used for signed runtime by entity // index. // // Value is empty. - signedRuntimeByEntityKeyFmt = keyformat.New(0x19, &signature.PublicKey{}, &common.Namespace{}) + signedRuntimeByEntityKeyFmt = keyformat.New(0x19, keyformat.H(&signature.PublicKey{}), keyformat.H(&common.Namespace{})) ) // ImmutableState is the immutable registry state wrapper. @@ -221,6 +222,7 @@ func (s *ImmutableState) Nodes(ctx context.Context) ([]*node.Node, error) { if it.Err() != nil { return nil, abciAPI.UnavailableStateError(it.Err()) } + registry.SortNodeList(nodes) return nodes, nil } @@ -441,39 +443,15 @@ func (s *ImmutableState) NodeStatus(ctx context.Context, id signature.PublicKey) return &status, nil } -// NodeStatuses returns all of the node statuses. -func (s *ImmutableState) NodeStatuses(ctx context.Context) (map[signature.PublicKey]*registry.NodeStatus, error) { - it := s.is.NewIterator(ctx) - defer it.Close() - - statuses := make(map[signature.PublicKey]*registry.NodeStatus) - for it.Seek(nodeStatusKeyFmt.Encode()); it.Valid(); it.Next() { - var nodeID signature.PublicKey - if !nodeStatusKeyFmt.Decode(it.Key(), &nodeID) { - break - } - - var status registry.NodeStatus - if err := cbor.Unmarshal(it.Value(), &status); err != nil { - return nil, abciAPI.UnavailableStateError(err) - } - - statuses[nodeID] = &status - } - if it.Err() != nil { - return nil, abciAPI.UnavailableStateError(it.Err()) - } - return statuses, nil -} - // HasEntityNodes checks whether an entity has any registered nodes. func (s *ImmutableState) HasEntityNodes(ctx context.Context, id signature.PublicKey) (bool, error) { it := s.is.NewIterator(ctx) defer it.Close() + hID := keyformat.PreHashed(id.Hash()) if it.Seek(signedNodeByEntityKeyFmt.Encode(&id)); it.Valid() { - var entityID signature.PublicKey - if !signedNodeByEntityKeyFmt.Decode(it.Key(), &entityID) || !entityID.Equal(id) { + var hEntityID keyformat.PreHashed + if !signedNodeByEntityKeyFmt.Decode(it.Key(), &hEntityID) || !hEntityID.Equal(&hID) { return false, nil } return true, nil @@ -486,9 +464,10 @@ func (s *ImmutableState) HasEntityRuntimes(ctx context.Context, id signature.Pub it := s.is.NewIterator(ctx) defer it.Close() + hID := keyformat.PreHashed(id.Hash()) if it.Seek(signedRuntimeByEntityKeyFmt.Encode(&id)); it.Valid() { - var entityID signature.PublicKey - if !signedRuntimeByEntityKeyFmt.Decode(it.Key(), &entityID) || !entityID.Equal(id) { + var hEntityID keyformat.PreHashed + if !signedRuntimeByEntityKeyFmt.Decode(it.Key(), &hEntityID) || !hEntityID.Equal(&hID) { return false, nil } return true, nil diff --git a/go/consensus/tendermint/apps/roothash/state/state.go b/go/consensus/tendermint/apps/roothash/state/state.go index 94d9629005d..f8d568fe3e8 100644 --- a/go/consensus/tendermint/apps/roothash/state/state.go +++ b/go/consensus/tendermint/apps/roothash/state/state.go @@ -19,7 +19,7 @@ var ( // runtimeKeyFmt is the key format used for per-runtime roothash state. // // Value is CBOR-serialized runtime state. - runtimeKeyFmt = keyformat.New(0x20, &common.Namespace{}) + runtimeKeyFmt = keyformat.New(0x20, keyformat.H(&common.Namespace{})) // parametersKeyFmt is the key format used for consensus parameters. // // Value is CBOR-serialized roothash.ConsensusParameters. diff --git a/go/consensus/tendermint/apps/scheduler/state/state.go b/go/consensus/tendermint/apps/scheduler/state/state.go index 74e58ac9a01..7a941671328 100644 --- a/go/consensus/tendermint/apps/scheduler/state/state.go +++ b/go/consensus/tendermint/apps/scheduler/state/state.go @@ -17,7 +17,7 @@ var ( // committeeKeyFmt is the key format used for committees. // // Value is CBOR-serialized committee. - committeeKeyFmt = keyformat.New(0x60, uint8(0), &common.Namespace{}) + committeeKeyFmt = keyformat.New(0x60, uint8(0), keyformat.H(&common.Namespace{})) // validatorsCurrentKeyFmt is the key format used for the current set of // validators. // @@ -64,14 +64,14 @@ func (s *ImmutableState) AllCommittees(ctx context.Context) ([]*api.Committee, e var committees []*api.Committee for it.Seek(committeeKeyFmt.Encode()); it.Valid(); it.Next() { var k uint8 - var runtimeID common.Namespace - if !committeeKeyFmt.Decode(it.Key(), &k, &runtimeID) { + var hRuntimeID keyformat.PreHashed + if !committeeKeyFmt.Decode(it.Key(), &k, &hRuntimeID) { break } var c api.Committee if err := cbor.Unmarshal(it.Value(), &c); err != nil { - err = fmt.Errorf("malformed committee %s (kind %d): %w", runtimeID, k, err) + err = fmt.Errorf("malformed committee %s (kind %d): %w", hRuntimeID, k, err) return nil, abciAPI.UnavailableStateError(err) } @@ -92,14 +92,14 @@ func (s *ImmutableState) KindsCommittees(ctx context.Context, kinds []api.Commit for _, kind := range kinds { for it.Seek(committeeKeyFmt.Encode(uint8(kind))); it.Valid(); it.Next() { var k uint8 - var runtimeID common.Namespace - if !committeeKeyFmt.Decode(it.Key(), &k, &runtimeID) || k != uint8(kind) { + var hRuntimeID keyformat.PreHashed + if !committeeKeyFmt.Decode(it.Key(), &k, &hRuntimeID) || k != uint8(kind) { break } var c api.Committee if err := cbor.Unmarshal(it.Value(), &c); err != nil { - err = fmt.Errorf("malformed committee %s (kind %d): %w", runtimeID, k, err) + err = fmt.Errorf("malformed committee %s (kind %d): %w", hRuntimeID, k, err) return nil, abciAPI.UnavailableStateError(err) }