Skip to content

Commit c041d43

Browse files
committed
[FAB-3245] Use crypto rand in gossip
In gossip the random uses math instead of crypto, and it is initialized with a static seed of 42 instead of something more random. This has an implication that the randomized selection is not so random. We should replace it with a crypto random for more security. Change-Id: Iaa28c14410a19d076b301d76b4539769c03f4764 Signed-off-by: Yacov Manevich <[email protected]>
1 parent d7af6e8 commit c041d43

File tree

7 files changed

+117
-24
lines changed

7 files changed

+117
-24
lines changed

gossip/comm/comm_impl.go

+2-7
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"crypto/tls"
2222
"errors"
2323
"fmt"
24-
"math/rand"
2524
"net"
2625
"os"
2726
"reflect"
@@ -52,10 +51,6 @@ const (
5251

5352
var errSendOverflow = errors.New(sendOverflowErr)
5453

55-
func init() {
56-
rand.Seed(42)
57-
}
58-
5954
// SetDialTimeout sets the dial timeout
6055
func SetDialTimeout(timeout time.Duration) {
6156
viper.Set("peer.gossip.dialTimeout", timeout)
@@ -604,8 +599,8 @@ func createGRPCLayer(port int) (*grpc.Server, net.Listener, grpc.DialOption, []b
604599
var serverOpts []grpc.ServerOption
605600
var dialOpts grpc.DialOption
606601

607-
keyFileName := fmt.Sprintf("key.%d.pem", rand.Int63())
608-
certFileName := fmt.Sprintf("cert.%d.pem", rand.Int63())
602+
keyFileName := fmt.Sprintf("key.%d.pem", util.RandomUInt64())
603+
certFileName := fmt.Sprintf("cert.%d.pem", util.RandomUInt64())
609604

610605
defer os.Remove(keyFileName)
611606
defer os.Remove(certFileName)

gossip/comm/comm_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import (
4343
)
4444

4545
func init() {
46-
rand.Seed(42)
46+
rand.Seed(time.Now().UnixNano())
4747
factory.InitFactories(nil)
4848
}
4949

gossip/gossip/algo/pull.go

+2-7
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ limitations under the License.
1717
package algo
1818

1919
import (
20-
"math/rand"
2120
"sync"
2221
"sync/atomic"
2322
"time"
@@ -44,10 +43,6 @@ import (
4443
4544
*/
4645

47-
func init() {
48-
rand.Seed(42)
49-
}
50-
5146
const (
5247
defDigestWaitTime = time.Duration(1) * time.Second
5348
defRequestWaitTime = time.Duration(1) * time.Second
@@ -214,7 +209,7 @@ func (engine *PullEngine) processIncomingDigests() {
214209
requestMapping := make(map[string][]string)
215210
for n, sources := range engine.item2owners {
216211
// select a random source
217-
source := sources[rand.Intn(len(sources))]
212+
source := sources[util.RandomInt(len(sources))]
218213
if _, exists := requestMapping[source]; !exists {
219214
requestMapping[source] = make([]string, 0)
220215
}
@@ -341,7 +336,7 @@ func (engine *PullEngine) OnRes(items []string, nonce uint64) {
341336
func (engine *PullEngine) newNONCE() uint64 {
342337
n := uint64(0)
343338
for {
344-
n = uint64(rand.Int63())
339+
n = util.RandomUInt64()
345340
if !engine.outgoingNONCES.Exists(n) {
346341
return n
347342
}

gossip/gossip/msgstore/msgs_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import (
2929
)
3030

3131
func init() {
32-
rand.Seed(42)
32+
rand.Seed(time.Now().UnixNano())
3333
}
3434

3535
func alwaysNoAction(this interface{}, that interface{}) common.InvalidationResult {

gossip/state/state.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package state
1919
import (
2020
"bytes"
2121
"errors"
22-
"math/rand"
2322
"sync"
2423
"sync/atomic"
2524
"time"
@@ -543,7 +542,7 @@ func (s *GossipStateProviderImpl) requestBlocksInRange(start uint64, end uint64)
543542
// Generate state request message for given blocks in range [beginSeq...endSeq]
544543
func (s *GossipStateProviderImpl) stateRequestMessage(beginSeq uint64, endSeq uint64) *proto.GossipMessage {
545544
return &proto.GossipMessage{
546-
Nonce: uint64(rand.Uint32())<<32 + uint64(rand.Uint32()),
545+
Nonce: util.RandomUInt64(),
547546
Tag: proto.GossipMessage_CHAN_OR_ORG,
548547
Channel: []byte(s.chainID),
549548
Content: &proto.GossipMessage_StateRequest{
@@ -566,7 +565,7 @@ func (s *GossipStateProviderImpl) selectPeerToRequestFrom(height uint64) (*comm.
566565
}
567566

568567
// Select peers to ask for blocks
569-
return peers[rand.Intn(n)], nil
568+
return peers[util.RandomInt(n)], nil
570569
}
571570

572571
// filterPeers return list of peers which aligns the predicate provided

gossip/util/misc.go

+29-5
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ limitations under the License.
1717
package util
1818

1919
import (
20+
cryptorand "crypto/rand"
2021
"fmt"
22+
"io"
23+
"math/big"
2124
"math/rand"
2225
"reflect"
2326
"runtime"
@@ -30,10 +33,6 @@ import (
3033
// Equals returns whether a and b are the same
3134
type Equals func(a interface{}, b interface{}) bool
3235

33-
func init() {
34-
rand.Seed(42)
35-
}
36-
3736
// IndexInSlice returns the index of given object o in array
3837
func IndexInSlice(array interface{}, o interface{}, equals Equals) int {
3938
arr := reflect.ValueOf(array)
@@ -65,7 +64,7 @@ func GetRandomIndices(indiceCount, highestIndex int) []int {
6564
}
6665

6766
for len(indices) < indiceCount {
68-
n := rand.Intn(highestIndex + 1)
67+
n := RandomInt(highestIndex + 1)
6968
if IndexInSlice(indices, n, numbericEqual) != -1 {
7069
continue
7170
}
@@ -159,3 +158,28 @@ func GetDurationOrDefault(key string, defVal time.Duration) time.Duration {
159158

160159
return defVal
161160
}
161+
162+
// RandomInt returns, as an int, a non-negative pseudo-random integer in [0,n)
163+
// It panics if n <= 0
164+
func RandomInt(n int) int {
165+
if n <= 0 {
166+
panic(fmt.Sprintf("Got invalid (non positive) value: %d", n))
167+
}
168+
m := int(RandomUInt64()) % n
169+
if m < 0 {
170+
return n + m
171+
}
172+
return m
173+
}
174+
175+
// RandomUInt64 returns a random uint64
176+
func RandomUInt64() uint64 {
177+
b := make([]byte, 8)
178+
_, err := io.ReadFull(cryptorand.Reader, b)
179+
if err == nil {
180+
n := new(big.Int)
181+
return n.SetBytes(b).Uint64()
182+
}
183+
rand.Seed(rand.Int63())
184+
return uint64(rand.Int63())
185+
}

gossip/util/misc_test.go

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
Copyright IBM Corp. 2017 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package util
18+
19+
import (
20+
"crypto/rand"
21+
"errors"
22+
"testing"
23+
24+
"github.com/stretchr/testify/assert"
25+
"github.com/stretchr/testify/mock"
26+
)
27+
28+
func testHappyPath(t *testing.T) {
29+
n1 := RandomInt(10000)
30+
n2 := RandomInt(10000)
31+
assert.NotEqual(t, n1, n2)
32+
n3 := RandomUInt64()
33+
n4 := RandomUInt64()
34+
assert.NotEqual(t, n3, n4)
35+
}
36+
37+
func TestGetRandomInt(t *testing.T) {
38+
testHappyPath(t)
39+
}
40+
41+
func TestNonNegativeValues(t *testing.T) {
42+
assert.True(t, RandomInt(1000000) >= 0)
43+
}
44+
45+
func TestGetRandomIntBadInput(t *testing.T) {
46+
f1 := func() {
47+
RandomInt(0)
48+
}
49+
f2 := func() {
50+
RandomInt(-500)
51+
}
52+
assert.Panics(t, f1)
53+
assert.Panics(t, f2)
54+
}
55+
56+
type reader struct {
57+
mock.Mock
58+
}
59+
60+
func (r *reader) Read(p []byte) (int, error) {
61+
args := r.Mock.Called(p)
62+
n := args.Get(0).(int)
63+
err := args.Get(1)
64+
if err == nil {
65+
return n, nil
66+
}
67+
return n, err.(error)
68+
}
69+
70+
func TestGetRandomIntNoEntropy(t *testing.T) {
71+
rr := rand.Reader
72+
defer func() {
73+
rand.Reader = rr
74+
}()
75+
r := &reader{}
76+
r.On("Read", mock.Anything).Return(0, errors.New("Not enough entropy"))
77+
rand.Reader = r
78+
// Make sure randomness still works even when we have no entropy
79+
testHappyPath(t)
80+
}

0 commit comments

Comments
 (0)