github.com/yimialmonte/fabric@v2.1.1+incompatible/gossip/gossip/certstore_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package gossip
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"sync"
    13  	"testing"
    14  	"time"
    15  
    16  	proto "github.com/hyperledger/fabric-protos-go/gossip"
    17  	"github.com/hyperledger/fabric/gossip/api"
    18  	"github.com/hyperledger/fabric/gossip/comm"
    19  	"github.com/hyperledger/fabric/gossip/common"
    20  	"github.com/hyperledger/fabric/gossip/discovery"
    21  	"github.com/hyperledger/fabric/gossip/gossip/algo"
    22  	"github.com/hyperledger/fabric/gossip/gossip/pull"
    23  	"github.com/hyperledger/fabric/gossip/identity"
    24  	"github.com/hyperledger/fabric/gossip/protoext"
    25  	"github.com/hyperledger/fabric/gossip/util"
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/mock"
    28  )
    29  
    30  func init() {
    31  	util.SetupTestLogging()
    32  }
    33  
    34  var (
    35  	cs = &naiveCryptoService{
    36  		revokedPkiIDS: make(map[string]struct{}),
    37  	}
    38  )
    39  
    40  type pullerMock struct {
    41  	mock.Mock
    42  	pull.Mediator
    43  }
    44  
    45  type sentMsg struct {
    46  	msg *protoext.SignedGossipMessage
    47  	mock.Mock
    48  }
    49  
    50  // GetSourceEnvelope Returns the SignedGossipMessage the ReceivedMessage was
    51  // constructed with
    52  func (s *sentMsg) GetSourceEnvelope() *proto.Envelope {
    53  	return nil
    54  }
    55  
    56  // Ack returns to the sender an acknowledgement for the message
    57  func (s *sentMsg) Ack(err error) {
    58  
    59  }
    60  
    61  func (s *sentMsg) Respond(msg *proto.GossipMessage) {
    62  	s.Called(msg)
    63  }
    64  
    65  func (s *sentMsg) GetGossipMessage() *protoext.SignedGossipMessage {
    66  	return s.msg
    67  }
    68  
    69  func (s *sentMsg) GetConnectionInfo() *protoext.ConnectionInfo {
    70  	return nil
    71  }
    72  
    73  type senderMock struct {
    74  	mock.Mock
    75  	sync.Mutex
    76  }
    77  
    78  func (s *senderMock) Send(msg *protoext.SignedGossipMessage, peers ...*comm.RemotePeer) {
    79  	s.Lock()
    80  	defer s.Unlock()
    81  	s.Called(msg, peers)
    82  }
    83  
    84  type membershipSvcMock struct {
    85  	mock.Mock
    86  }
    87  
    88  func (m *membershipSvcMock) GetMembership() []discovery.NetworkMember {
    89  	args := m.Called()
    90  	return args.Get(0).([]discovery.NetworkMember)
    91  }
    92  
    93  func TestCertStoreBadSignature(t *testing.T) {
    94  	badSignature := func(nonce uint64) protoext.ReceivedMessage {
    95  		return createUpdateMessage(nonce, createBadlySignedUpdateMessage())
    96  	}
    97  	pm, cs, _ := createObjects(badSignature, nil)
    98  	defer pm.Stop()
    99  	defer cs.stop()
   100  	testCertificateUpdate(t, false, cs)
   101  }
   102  
   103  func TestCertStoreMismatchedIdentity(t *testing.T) {
   104  	mismatchedIdentity := func(nonce uint64) protoext.ReceivedMessage {
   105  		return createUpdateMessage(nonce, createMismatchedUpdateMessage())
   106  	}
   107  
   108  	pm, cs, _ := createObjects(mismatchedIdentity, nil)
   109  	defer pm.Stop()
   110  	defer cs.stop()
   111  	testCertificateUpdate(t, false, cs)
   112  }
   113  
   114  func TestCertStoreShouldSucceed(t *testing.T) {
   115  	totallyFineIdentity := func(nonce uint64) protoext.ReceivedMessage {
   116  		return createUpdateMessage(nonce, createValidUpdateMessage())
   117  	}
   118  
   119  	pm, cs, _ := createObjects(totallyFineIdentity, nil)
   120  	defer pm.Stop()
   121  	defer cs.stop()
   122  	testCertificateUpdate(t, true, cs)
   123  }
   124  
   125  func TestCertRevocation(t *testing.T) {
   126  	defer func() {
   127  		cs.revokedPkiIDS = map[string]struct{}{}
   128  	}()
   129  
   130  	totallyFineIdentity := func(nonce uint64) protoext.ReceivedMessage {
   131  		return createUpdateMessage(nonce, createValidUpdateMessage())
   132  	}
   133  
   134  	askedForIdentity := make(chan struct{}, 1)
   135  
   136  	pm, cStore, sender := createObjects(totallyFineIdentity, func(message *protoext.SignedGossipMessage) {
   137  		askedForIdentity <- struct{}{}
   138  	})
   139  	defer cStore.stop()
   140  	defer pm.Stop()
   141  	testCertificateUpdate(t, true, cStore)
   142  	// Should have asked for an identity for the first time
   143  	assert.Len(t, askedForIdentity, 1)
   144  	// Drain channel
   145  	<-askedForIdentity
   146  	// Now it's 0
   147  	assert.Len(t, askedForIdentity, 0)
   148  
   149  	sentHello := false
   150  	l := sync.Mutex{}
   151  	sender.Lock()
   152  	sender.Mock = mock.Mock{}
   153  	sender.On("Send", mock.Anything, mock.Anything).Run(func(arg mock.Arguments) {
   154  		msg := arg.Get(0).(*protoext.SignedGossipMessage)
   155  		l.Lock()
   156  		defer l.Unlock()
   157  
   158  		if hello := msg.GetHello(); hello != nil && !sentHello {
   159  			sentHello = true
   160  			dig := &proto.GossipMessage{
   161  				Tag: proto.GossipMessage_EMPTY,
   162  				Content: &proto.GossipMessage_DataDig{
   163  					DataDig: &proto.DataDigest{
   164  						Nonce:   hello.Nonce,
   165  						MsgType: proto.PullMsgType_IDENTITY_MSG,
   166  						Digests: [][]byte{[]byte("B")},
   167  					},
   168  				},
   169  			}
   170  			sMsg, _ := protoext.NoopSign(dig)
   171  			go cStore.handleMessage(&sentMsg{msg: sMsg})
   172  		}
   173  
   174  		if dataReq := msg.GetDataReq(); dataReq != nil {
   175  			askedForIdentity <- struct{}{}
   176  		}
   177  	})
   178  	sender.Unlock()
   179  	testCertificateUpdate(t, true, cStore)
   180  	// Shouldn't have asked, because already got identity
   181  	select {
   182  	case <-time.After(time.Second * 5):
   183  	case <-askedForIdentity:
   184  		assert.Fail(t, "Shouldn't have asked for an identity, because we already have it")
   185  	}
   186  	assert.Len(t, askedForIdentity, 0)
   187  	// Revoke the identity
   188  	cs.revoke(common.PKIidType("B"))
   189  	cStore.suspectPeers(func(id api.PeerIdentityType) bool {
   190  		return string(id) == "B"
   191  	})
   192  
   193  	l.Lock()
   194  	sentHello = false
   195  	l.Unlock()
   196  
   197  	select {
   198  	case <-time.After(time.Second * 5):
   199  		assert.Fail(t, "Didn't ask for identity, but should have. Looks like identity hasn't expired")
   200  	case <-askedForIdentity:
   201  	}
   202  }
   203  
   204  func TestCertExpiration(t *testing.T) {
   205  	// Scenario: In this test we make sure that a peer may not expire
   206  	// its own identity.
   207  	// This is important because the only way identities are gossiped
   208  	// transitively is via the pull mechanism.
   209  	// If a peer's own identity disappears from the pull mediator,
   210  	// it will never be sent to peers transitively.
   211  	// The test ensures that self identities don't expire
   212  	// in the following manner:
   213  	// It starts a peer and then sleeps twice the identity usage threshold,
   214  	// in order to make sure that its own identity should be expired.
   215  	// Then, it starts another peer, and listens to the messages sent
   216  	// between both peers, and looks for a few identity digests of the first peer.
   217  	// If such identity digest are detected, it means that the peer
   218  	// didn't expire its own identity.
   219  
   220  	// Backup original usageThreshold value
   221  	idUsageThreshold := identity.GetIdentityUsageThreshold()
   222  	identity.SetIdentityUsageThreshold(time.Second)
   223  	// Restore original usageThreshold value
   224  	defer identity.SetIdentityUsageThreshold(idUsageThreshold)
   225  
   226  	port0, grpc0, certs0, secDialOpts0, _ := util.CreateGRPCLayer()
   227  	port1, grpc1, certs1, secDialOpts1, _ := util.CreateGRPCLayer()
   228  	g1 := newGossipInstanceWithGRPC(0, port0, grpc0, certs0, secDialOpts0, 0, port1)
   229  	defer g1.Stop()
   230  	time.Sleep(identity.GetIdentityUsageThreshold() * 2)
   231  	g2 := newGossipInstanceWithGRPC(0, port1, grpc1, certs1, secDialOpts1, 0)
   232  	defer g2.Stop()
   233  
   234  	identities2Detect := 3
   235  	// Make the channel bigger than needed so goroutines won't get stuck
   236  	identitiesGotViaPull := make(chan struct{}, identities2Detect+100)
   237  	acceptIdentityPullMsgs := func(o interface{}) bool {
   238  		m := o.(protoext.ReceivedMessage).GetGossipMessage()
   239  		if protoext.IsPullMsg(m.GossipMessage) && protoext.IsDigestMsg(m.GossipMessage) {
   240  			for _, dig := range m.GetDataDig().Digests {
   241  				if bytes.Equal(dig, []byte(fmt.Sprintf("127.0.0.1:%d", port0))) {
   242  					identitiesGotViaPull <- struct{}{}
   243  				}
   244  			}
   245  		}
   246  		return false
   247  	}
   248  	g1.Accept(acceptIdentityPullMsgs, true)
   249  	for i := 0; i < identities2Detect; i++ {
   250  		select {
   251  		case <-identitiesGotViaPull:
   252  		case <-time.After(time.Second * 15):
   253  			assert.Fail(t, "Didn't detect an identity gossiped via pull in a timely manner")
   254  			return
   255  		}
   256  	}
   257  }
   258  
   259  func testCertificateUpdate(t *testing.T, shouldSucceed bool, certStore *certStore) {
   260  	msg, _ := protoext.NoopSign(&proto.GossipMessage{
   261  		Channel: []byte(""),
   262  		Tag:     proto.GossipMessage_EMPTY,
   263  		Content: &proto.GossipMessage_Hello{
   264  			Hello: &proto.GossipHello{
   265  				Nonce:    0,
   266  				Metadata: nil,
   267  				MsgType:  proto.PullMsgType_IDENTITY_MSG,
   268  			},
   269  		},
   270  	})
   271  	hello := &sentMsg{
   272  		msg: msg,
   273  	}
   274  	responseChan := make(chan *proto.GossipMessage, 1)
   275  	hello.On("Respond", mock.Anything).Run(func(arg mock.Arguments) {
   276  		msg := arg.Get(0).(*proto.GossipMessage)
   277  		assert.NotNil(t, msg.GetDataDig())
   278  		responseChan <- msg
   279  	})
   280  	certStore.handleMessage(hello)
   281  	select {
   282  	case msg := <-responseChan:
   283  		if shouldSucceed {
   284  			assert.Len(t, msg.GetDataDig().Digests, 2, "Valid identity hasn't entered the certStore")
   285  		} else {
   286  			assert.Len(t, msg.GetDataDig().Digests, 1, "Mismatched identity has been injected into certStore")
   287  		}
   288  	case <-time.After(time.Second):
   289  		t.Fatal("Didn't respond with a digest message in a timely manner")
   290  	}
   291  }
   292  
   293  func createMismatchedUpdateMessage() *protoext.SignedGossipMessage {
   294  	peeridentity := &proto.PeerIdentity{
   295  		// This PKI-ID is different than the cert, and the mapping between
   296  		// certificate to PKI-ID in this test is simply the identity function.
   297  		PkiId: []byte("A"),
   298  		Cert:  []byte("D"),
   299  	}
   300  
   301  	signer := func(msg []byte) ([]byte, error) {
   302  		return (&naiveCryptoService{}).Sign(msg)
   303  	}
   304  	m := &proto.GossipMessage{
   305  		Channel: nil,
   306  		Nonce:   0,
   307  		Tag:     proto.GossipMessage_EMPTY,
   308  		Content: &proto.GossipMessage_PeerIdentity{
   309  			PeerIdentity: peeridentity,
   310  		},
   311  	}
   312  	sMsg := &protoext.SignedGossipMessage{
   313  		GossipMessage: m,
   314  	}
   315  	sMsg.Sign(signer)
   316  	return sMsg
   317  }
   318  
   319  func createBadlySignedUpdateMessage() *protoext.SignedGossipMessage {
   320  	peeridentity := &proto.PeerIdentity{
   321  		PkiId: []byte("C"),
   322  		Cert:  []byte("C"),
   323  	}
   324  
   325  	signer := func(msg []byte) ([]byte, error) {
   326  		return (&naiveCryptoService{}).Sign(msg)
   327  	}
   328  
   329  	m := &proto.GossipMessage{
   330  		Channel: nil,
   331  		Nonce:   0,
   332  		Tag:     proto.GossipMessage_EMPTY,
   333  		Content: &proto.GossipMessage_PeerIdentity{
   334  			PeerIdentity: peeridentity,
   335  		},
   336  	}
   337  	sMsg := &protoext.SignedGossipMessage{
   338  		GossipMessage: m,
   339  	}
   340  	sMsg.Sign(signer)
   341  	// This would simulate a bad sig
   342  	if sMsg.Envelope.Signature[0] == 0 {
   343  		sMsg.Envelope.Signature[0] = 1
   344  	} else {
   345  		sMsg.Envelope.Signature[0] = 0
   346  	}
   347  	return sMsg
   348  }
   349  
   350  func createValidUpdateMessage() *protoext.SignedGossipMessage {
   351  	peeridentity := &proto.PeerIdentity{
   352  		PkiId: []byte("B"),
   353  		Cert:  []byte("B"),
   354  	}
   355  
   356  	signer := func(msg []byte) ([]byte, error) {
   357  		return (&naiveCryptoService{}).Sign(msg)
   358  	}
   359  	m := &proto.GossipMessage{
   360  		Channel: nil,
   361  		Nonce:   0,
   362  		Tag:     proto.GossipMessage_EMPTY,
   363  		Content: &proto.GossipMessage_PeerIdentity{
   364  			PeerIdentity: peeridentity,
   365  		},
   366  	}
   367  	sMsg := &protoext.SignedGossipMessage{
   368  		GossipMessage: m,
   369  	}
   370  	sMsg.Sign(signer)
   371  	return sMsg
   372  }
   373  
   374  func createUpdateMessage(nonce uint64, idMsg *protoext.SignedGossipMessage) protoext.ReceivedMessage {
   375  	update := &proto.GossipMessage{
   376  		Tag: proto.GossipMessage_EMPTY,
   377  		Content: &proto.GossipMessage_DataUpdate{
   378  			DataUpdate: &proto.DataUpdate{
   379  				MsgType: proto.PullMsgType_IDENTITY_MSG,
   380  				Nonce:   nonce,
   381  				Data:    []*proto.Envelope{idMsg.Envelope},
   382  			},
   383  		},
   384  	}
   385  	sMsg, _ := protoext.NoopSign(update)
   386  	return &sentMsg{msg: sMsg}
   387  }
   388  
   389  func createDigest(nonce uint64) protoext.ReceivedMessage {
   390  	digest := &proto.GossipMessage{
   391  		Tag: proto.GossipMessage_EMPTY,
   392  		Content: &proto.GossipMessage_DataDig{
   393  			DataDig: &proto.DataDigest{
   394  				Nonce:   nonce,
   395  				MsgType: proto.PullMsgType_IDENTITY_MSG,
   396  				Digests: [][]byte{[]byte("A"), []byte("C")},
   397  			},
   398  		},
   399  	}
   400  	sMsg, _ := protoext.NoopSign(digest)
   401  	return &sentMsg{msg: sMsg}
   402  }
   403  
   404  func createObjects(updateFactory func(uint64) protoext.ReceivedMessage, msgCons pull.MsgConsumer) (pull.Mediator, *certStore, *senderMock) {
   405  	if msgCons == nil {
   406  		msgCons = func(_ *protoext.SignedGossipMessage) {}
   407  	}
   408  	shortenedWaitTime := time.Millisecond * 300
   409  	config := pull.Config{
   410  		MsgType:           proto.PullMsgType_IDENTITY_MSG,
   411  		PeerCountToSelect: 1,
   412  		PullInterval:      time.Second,
   413  		Tag:               proto.GossipMessage_EMPTY,
   414  		Channel:           nil,
   415  		ID:                "id1",
   416  		PullEngineConfig: algo.PullEngineConfig{
   417  			DigestWaitTime:   shortenedWaitTime / 2,
   418  			RequestWaitTime:  shortenedWaitTime,
   419  			ResponseWaitTime: shortenedWaitTime,
   420  		},
   421  	}
   422  	sender := &senderMock{}
   423  	memberSvc := &membershipSvcMock{}
   424  	memberSvc.On("GetMembership").Return([]discovery.NetworkMember{{PKIid: []byte("bla bla"), Endpoint: "127.0.0.1:5611"}})
   425  
   426  	var certStore *certStore
   427  	adapter := &pull.PullAdapter{
   428  		Sndr: sender,
   429  		MsgCons: func(msg *protoext.SignedGossipMessage) {
   430  			certStore.idMapper.Put(msg.GetPeerIdentity().PkiId, msg.GetPeerIdentity().Cert)
   431  			msgCons(msg)
   432  		},
   433  		IdExtractor: func(msg *protoext.SignedGossipMessage) string {
   434  			return string(msg.GetPeerIdentity().PkiId)
   435  		},
   436  		MemSvc: memberSvc,
   437  	}
   438  	pullMediator := pull.NewPullMediator(config, adapter)
   439  	selfIdentity := api.PeerIdentityType("SELF")
   440  	certStore = newCertStore(&pullerMock{
   441  		Mediator: pullMediator,
   442  	}, identity.NewIdentityMapper(cs, selfIdentity, func(pkiID common.PKIidType, _ api.PeerIdentityType) {
   443  		pullMediator.Remove(string(pkiID))
   444  	}, cs), selfIdentity, cs)
   445  
   446  	wg := sync.WaitGroup{}
   447  	wg.Add(1)
   448  	sentHello := false
   449  	sentDataReq := false
   450  	l := sync.Mutex{}
   451  	sender.On("Send", mock.Anything, mock.Anything).Run(func(arg mock.Arguments) {
   452  		msg := arg.Get(0).(*protoext.SignedGossipMessage)
   453  		l.Lock()
   454  		defer l.Unlock()
   455  
   456  		if hello := msg.GetHello(); hello != nil && !sentHello {
   457  			sentHello = true
   458  			go certStore.handleMessage(createDigest(hello.Nonce))
   459  		}
   460  
   461  		if dataReq := msg.GetDataReq(); dataReq != nil && !sentDataReq {
   462  			sentDataReq = true
   463  			certStore.handleMessage(updateFactory(dataReq.Nonce))
   464  			wg.Done()
   465  		}
   466  	})
   467  	wg.Wait()
   468  	return pullMediator, certStore, sender
   469  }