Skip to content

Commit

Permalink
QSP-6: Enforces crypto-secure PRNGs (#6401)
Browse files Browse the repository at this point in the history
* adds cryptorand analyzer

* better naming

* rely on suffix

* sync/pending_* use crypto/rand

* define shared/rand

* updates fetcher

* fixes rand issue in sync package

* gofmt

* shared/rand: more docs + add exclusion nogo_config.json

* updates validator/assignments

* updates comment

* fixes remaning cases

* re-arranges comments

* fixes tests

* renames in shared/rand API

* adds simple no-panic test

* gazelle

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
  • Loading branch information
farazdagi and prylabs-bulldozer[bot] authored Jun 26, 2020
1 parent 73f3a65 commit 78465e2
Show file tree
Hide file tree
Showing 28 changed files with 360 additions and 92 deletions.
1 change: 1 addition & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ nogo(
"@org_golang_x_tools//go/analysis/passes/asmdecl:go_tool_library",
"//tools/analyzers/maligned:go_tool_library",
"//tools/analyzers/roughtime:go_tool_library",
"//tools/analyzers/cryptorand:go_tool_library",
"//tools/analyzers/errcheck:go_tool_library",
"//tools/analyzers/featureconfig:go_tool_library",
] + select({
Expand Down
1 change: 1 addition & 0 deletions beacon-chain/db/testing/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ go_library(
"//beacon-chain/cache:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/db/kv:go_default_library",
"//shared/rand:go_default_library",
"//shared/testutil:go_default_library",
],
)
8 changes: 2 additions & 6 deletions beacon-chain/db/testing/setup_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,21 @@
package testing

import (
"crypto/rand"
"fmt"
"math/big"
"os"
"path"
"testing"

"github.com/prysmaticlabs/prysm/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
"github.com/prysmaticlabs/prysm/shared/rand"
"github.com/prysmaticlabs/prysm/shared/testutil"
)

// SetupDB instantiates and returns database backed by key value store.
func SetupDB(t testing.TB) (db.Database, *cache.StateSummaryCache) {
randPath, err := rand.Int(rand.Reader, big.NewInt(1000000))
if err != nil {
t.Fatalf("could not generate random file path: %v", err)
}
randPath := rand.NewDeterministicGenerator().Int()
p := path.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath))
if err := os.RemoveAll(p); err != nil {
t.Fatalf("failed to remove directory: %v", err)
Expand Down
3 changes: 0 additions & 3 deletions beacon-chain/rpc/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ package rpc
import (
"context"
"fmt"
"math/rand"
"net"
"os"

middleware "github.com/grpc-ecosystem/go-grpc-middleware"
recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
Expand Down Expand Up @@ -50,7 +48,6 @@ var log logrus.FieldLogger

func init() {
log = logrus.WithField("prefix", "rpc")
rand.Seed(int64(os.Getpid()))
}

// Service defining an RPC server for a beacon node.
Expand Down
1 change: 1 addition & 0 deletions beacon-chain/rpc/validator/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ go_library(
"//shared/featureconfig:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/params:go_default_library",
"//shared/rand:go_default_library",
"//shared/roughtime:go_default_library",
"//shared/slotutil:go_default_library",
"//shared/traceutil:go_default_library",
Expand Down
35 changes: 11 additions & 24 deletions beacon-chain/rpc/validator/assignments.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@ package validator

import (
"context"
"crypto/rand"
"math/big"
"time"

"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
Expand All @@ -15,6 +12,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/rand"
"github.com/prysmaticlabs/prysm/shared/roughtime"
"github.com/prysmaticlabs/prysm/shared/slotutil"
"google.golang.org/grpc/codes"
Expand Down Expand Up @@ -178,12 +176,8 @@ func (vs *Server) duties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.
validatorAssignments = append(validatorAssignments, assignment)
nextValidatorAssignments = append(nextValidatorAssignments, nextAssignment)
// Assign relevant validator to subnet.
if err := assignValidatorToSubnet(pubKey, assignment.Status); err != nil {
return nil, errors.Wrap(err, "could not assign validator to subnet")
}
if err := assignValidatorToSubnet(pubKey, nextAssignment.Status); err != nil {
return nil, errors.Wrap(err, "could not assign validator to subnet")
}
assignValidatorToSubnet(pubKey, assignment.Status)
assignValidatorToSubnet(pubKey, nextAssignment.Status)
}

return &ethpb.DutiesResponse{
Expand All @@ -195,33 +189,26 @@ func (vs *Server) duties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.

// assignValidatorToSubnet checks the status and pubkey of a particular validator
// to discern whether persistent subnets need to be registered for them.
func assignValidatorToSubnet(pubkey []byte, status ethpb.ValidatorStatus) error {
func assignValidatorToSubnet(pubkey []byte, status ethpb.ValidatorStatus) {
if status != ethpb.ValidatorStatus_ACTIVE && status != ethpb.ValidatorStatus_EXITING {
return nil
return
}

_, ok, expTime := cache.SubnetIDs.GetPersistentSubnets(pubkey)
if ok && expTime.After(roughtime.Now()) {
return nil
return
}
epochDuration := time.Duration(params.BeaconConfig().SlotsPerEpoch * params.BeaconConfig().SecondsPerSlot)
assignedIdxs := []uint64{}
randGen := rand.NewGenerator()
for i := uint64(0); i < params.BeaconNetworkConfig().RandomSubnetsPerValidator; i++ {
randInt, err := rand.Int(rand.Reader, big.NewInt(int64(params.BeaconNetworkConfig().AttestationSubnetCount)))
if err != nil {
return errors.Wrap(err, "could not get random subnet index")
}
assignedIdxs = append(assignedIdxs, randInt.Uint64())
assignedIdx := randGen.Intn(int(params.BeaconNetworkConfig().AttestationSubnetCount))
assignedIdxs = append(assignedIdxs, uint64(assignedIdx))
}

randInt, err := rand.Int(rand.Reader, big.NewInt(int64(params.BeaconNetworkConfig().EpochsPerRandomSubnetSubscription)))
if err != nil {
return errors.Wrap(err, "could not get random subnet duration")
}
assignedDuration := randInt.Int64()
assignedDuration += int64(params.BeaconNetworkConfig().EpochsPerRandomSubnetSubscription)
assignedDuration := randGen.Intn(int(params.BeaconNetworkConfig().EpochsPerRandomSubnetSubscription))
assignedDuration += int(params.BeaconNetworkConfig().EpochsPerRandomSubnetSubscription)

totalDuration := epochDuration * time.Duration(assignedDuration)
cache.SubnetIDs.AddPersistentCommittee(pubkey, assignedIdxs, totalDuration*time.Second)
return nil
}
4 changes: 1 addition & 3 deletions beacon-chain/rpc/validator/assignments_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -467,9 +467,7 @@ func TestStreamDuties_OK_ChainReorg(t *testing.T) {
func TestAssignValidatorToSubnet(t *testing.T) {
k := pubKey(3)

if err := assignValidatorToSubnet(k, ethpb.ValidatorStatus_ACTIVE); err != nil {
t.Fatal(err)
}
assignValidatorToSubnet(k, ethpb.ValidatorStatus_ACTIVE)
coms, ok, exp := cache.SubnetIDs.GetPersistentSubnets(k)
if !ok {
t.Fatal("No cache entry found for validator")
Expand Down
17 changes: 4 additions & 13 deletions beacon-chain/rpc/validator/proposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package validator

import (
"context"
"crypto/rand"
"fmt"
"math/big"
"time"
Expand All @@ -23,6 +22,7 @@ import (
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/rand"
"github.com/prysmaticlabs/prysm/shared/trieutil"
"go.opencensus.io/trace"
"google.golang.org/grpc/codes"
Expand Down Expand Up @@ -223,18 +223,9 @@ func (vs *Server) randomETH1DataVote(ctx context.Context) (*ethpb.Eth1Data, erro
}
// set random roots and block hashes to prevent a majority from being
// built if the eth1 node is offline
randomDepBytes := make([]byte, 32)
randomBlkBytes := make([]byte, 32)
_, err = rand.Read(randomDepBytes)
if err != nil {
return nil, err
}
_, err = rand.Read(randomBlkBytes)
if err != nil {
return nil, err
}
depRoot := hashutil.Hash(randomDepBytes)
blockHash := hashutil.Hash(randomBlkBytes)
randGen := rand.NewGenerator()
depRoot := hashutil.Hash(bytesutil.Bytes32(randGen.Uint64()))
blockHash := hashutil.Hash(bytesutil.Bytes32(randGen.Uint64()))
return &ethpb.Eth1Data{
DepositRoot: depRoot[:],
DepositCount: headState.Eth1DepositIndex(),
Expand Down
2 changes: 1 addition & 1 deletion beacon-chain/sync/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ go_library(
"//shared/messagehandler:go_default_library",
"//shared/p2putils:go_default_library",
"//shared/params:go_default_library",
"//shared/rand:go_default_library",
"//shared/roughtime:go_default_library",
"//shared/runutil:go_default_library",
"//shared/sliceutil:go_default_library",
Expand All @@ -89,7 +90,6 @@ go_library(
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
"@org_golang_x_exp//rand:go_default_library",
],
)

Expand Down
1 change: 1 addition & 0 deletions beacon-chain/sync/initial-sync/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ go_library(
"//shared/bytesutil:go_default_library",
"//shared/mathutil:go_default_library",
"//shared/params:go_default_library",
"//shared/rand:go_default_library",
"//shared/roughtime:go_default_library",
"@com_github_kevinms_leakybucket_go//:go_default_library",
"@com_github_libp2p_go_libp2p_core//peer:go_default_library",
Expand Down
26 changes: 6 additions & 20 deletions beacon-chain/sync/initial-sync/blocks_fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@ package initialsync

import (
"context"
"crypto/rand"
"fmt"
"io"
"math"
"math/big"
mathRand "math/rand"
"sort"
"sync"
"time"
Expand All @@ -24,6 +21,7 @@ import (
p2ppb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/mathutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/rand"
"github.com/prysmaticlabs/prysm/shared/roughtime"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
Expand Down Expand Up @@ -64,6 +62,7 @@ type blocksFetcher struct {
sync.Mutex
ctx context.Context
cancel context.CancelFunc
rand *rand.Rand
headFetcher blockchain.HeadFetcher
p2p p2p.P2P
blocksPerSecond uint64
Expand Down Expand Up @@ -108,6 +107,7 @@ func newBlocksFetcher(ctx context.Context, cfg *blocksFetcherConfig) *blocksFetc
return &blocksFetcher{
ctx: ctx,
cancel: cancel,
rand: rand.NewGenerator(),
headFetcher: cfg.headFetcher,
p2p: cfg.p2p,
blocksPerSecond: uint64(blocksPerSecond),
Expand Down Expand Up @@ -387,11 +387,7 @@ func (f *blocksFetcher) selectFailOverPeer(excludedPID peer.ID, peers []peer.ID)
return "", errNoPeersAvailable
}

randInt, err := rand.Int(rand.Reader, big.NewInt(int64(len(peers))))
if err != nil {
return "", err
}
return peers[randInt.Int64()], nil
return peers[f.rand.Int()%len(peers)], nil
}

// waitForMinimumPeers spins and waits up until enough peers are available.
Expand Down Expand Up @@ -425,12 +421,7 @@ func (f *blocksFetcher) filterPeers(peers []peer.ID, peersPercentage float64) ([

// Shuffle peers to prevent a bad peer from
// stalling sync with invalid blocks.
randSource, err := rand.Int(rand.Reader, big.NewInt(roughtime.Now().Unix()))
if err != nil {
return nil, errors.Wrap(err, "could not generate random int")
}
randGenerator := mathRand.New(mathRand.NewSource(randSource.Int64()))
randGenerator.Shuffle(len(peers), func(i, j int) {
f.rand.Shuffle(len(peers), func(i, j int) {
peers[i], peers[j] = peers[j], peers[i]
})

Expand Down Expand Up @@ -478,11 +469,6 @@ func (f *blocksFetcher) nonSkippedSlotAfter(ctx context.Context, slot uint64) (u
return 0, errNoPeersAvailable
}

randSource, err := rand.Int(rand.Reader, big.NewInt(roughtime.Now().Unix()))
if err != nil {
return 0, errors.Wrap(err, "could not generate random int")
}
randGenerator := mathRand.New(mathRand.NewSource(randSource.Int64()))
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
pidInd := 0

Expand Down Expand Up @@ -525,7 +511,7 @@ func (f *blocksFetcher) nonSkippedSlotAfter(ctx context.Context, slot uint64) (u
slot = slot + nonSkippedSlotsFullSearchEpochs*slotsPerEpoch
upperBoundSlot := helpers.StartSlot(epoch + 1)
for ind := slot + 1; ind < upperBoundSlot; ind += (slotsPerEpoch * slotsPerEpoch) / 2 {
start := ind + uint64(randGenerator.Intn(int(slotsPerEpoch)))
start := ind + uint64(f.rand.Intn(int(slotsPerEpoch)))
nextSlot, err := fetch(peers[pidInd%len(peers)], start, slotsPerEpoch/2, slotsPerEpoch)
if err != nil {
return 0, err
Expand Down
5 changes: 3 additions & 2 deletions beacon-chain/sync/pending_attestations_queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ import (
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/rand"
"github.com/prysmaticlabs/prysm/shared/runutil"
"github.com/prysmaticlabs/prysm/shared/slotutil"
"github.com/prysmaticlabs/prysm/shared/traceutil"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
"golang.org/x/exp/rand"
)

// This defines how often a node cleans up and processes pending attestations in the queue.
Expand Down Expand Up @@ -60,6 +60,7 @@ func (s *Service) processPendingAtts(ctx context.Context) error {
}
s.pendingAttsLock.RUnlock()

randGen := rand.NewGenerator()
for _, bRoot := range roots {
s.pendingAttsLock.RLock()
attestations := s.blkRootToPendingAtts[bRoot]
Expand Down Expand Up @@ -130,7 +131,7 @@ func (s *Service) processPendingAtts(ctx context.Context) error {
log.Debug("No peer IDs available to request missing block from for pending attestation")
return nil
}
pid := pids[rand.Int()%len(pids)]
pid := pids[randGen.Int()%len(pids)]
targetSlot := helpers.SlotToEpoch(attestations[0].Message.Aggregate.Data.Target.Epoch)
for _, p := range pids {
cs, err := s.p2p.Peers().ChainState(p)
Expand Down
5 changes: 3 additions & 2 deletions beacon-chain/sync/pending_blocks_queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/rand"
"github.com/prysmaticlabs/prysm/shared/runutil"
"github.com/prysmaticlabs/prysm/shared/slotutil"
"github.com/prysmaticlabs/prysm/shared/traceutil"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
"golang.org/x/exp/rand"
)

var processPendingBlocksPeriod = slotutil.DivideSlotBy(3 /* times per slot */)
Expand Down Expand Up @@ -51,6 +51,7 @@ func (s *Service) processPendingBlocks(ctx context.Context) error {
trace.Int64Attribute("numPeers", int64(len(pids))),
)

randGen := rand.NewGenerator()
for _, slot := range slots {
ctx, span := trace.StartSpan(ctx, "processPendingBlocks.InnerLoop")
span.AddAttributes(trace.Int64Attribute("slot", int64(slot)))
Expand Down Expand Up @@ -80,7 +81,7 @@ func (s *Service) processPendingBlocks(ctx context.Context) error {

// Start with a random peer to query, but choose the first peer in our unsorted list that claims to
// have a head slot newer than the block slot we are requesting.
pid := pids[rand.Int()%len(pids)]
pid := pids[randGen.Int()%len(pids)]
for _, p := range pids {
cs, err := s.p2p.Peers().ChainState(p)
if err != nil {
Expand Down
Loading

0 comments on commit 78465e2

Please sign in to comment.