github.com/defanghe/fabric@v2.1.1+incompatible/gossip/identity/identity_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package identity
     8  
     9  import (
    10  	"bytes"
    11  	"errors"
    12  	"fmt"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  
    17  	cb "github.com/hyperledger/fabric-protos-go/common"
    18  	"github.com/hyperledger/fabric/gossip/api"
    19  	"github.com/hyperledger/fabric/gossip/common"
    20  	"github.com/hyperledger/fabric/gossip/util"
    21  	"github.com/stretchr/testify/assert"
    22  	"github.com/stretchr/testify/mock"
    23  )
    24  
    25  var (
    26  	msgCryptoService = &naiveCryptoService{revokedIdentities: map[string]struct{}{}}
    27  	dummyID          = api.PeerIdentityType("dummyID")
    28  )
    29  
    30  type naiveCryptoService struct {
    31  	mock.Mock
    32  	revokedIdentities map[string]struct{}
    33  }
    34  
    35  var noopPurgeTrigger = func(_ common.PKIidType, _ api.PeerIdentityType) {}
    36  
    37  func init() {
    38  	util.SetupTestLogging()
    39  	msgCryptoService.On("Expiration", api.PeerIdentityType(dummyID)).Return(time.Now().Add(time.Hour), nil)
    40  	msgCryptoService.On("Expiration", api.PeerIdentityType("yacovm")).Return(time.Now().Add(time.Hour), nil)
    41  	msgCryptoService.On("Expiration", api.PeerIdentityType("not-yacovm")).Return(time.Now().Add(time.Hour), nil)
    42  	msgCryptoService.On("Expiration", api.PeerIdentityType("invalidIdentity")).Return(time.Now().Add(time.Hour), nil)
    43  }
    44  
    45  func (cs *naiveCryptoService) OrgByPeerIdentity(id api.PeerIdentityType) api.OrgIdentityType {
    46  	found := false
    47  	for _, call := range cs.Mock.ExpectedCalls {
    48  		if call.Method == "OrgByPeerIdentity" {
    49  			found = true
    50  		}
    51  	}
    52  	if !found {
    53  		return nil
    54  	}
    55  	return cs.Called(id).Get(0).(api.OrgIdentityType)
    56  }
    57  
    58  func (cs *naiveCryptoService) Expiration(peerIdentity api.PeerIdentityType) (time.Time, error) {
    59  	args := cs.Called(peerIdentity)
    60  	t, err := args.Get(0), args.Get(1)
    61  	if err == nil {
    62  		return t.(time.Time), nil
    63  	}
    64  	return time.Time{}, err.(error)
    65  }
    66  
    67  func (cs *naiveCryptoService) ValidateIdentity(peerIdentity api.PeerIdentityType) error {
    68  	if _, isRevoked := cs.revokedIdentities[string(cs.GetPKIidOfCert(peerIdentity))]; isRevoked {
    69  		return errors.New("revoked")
    70  	}
    71  	return nil
    72  }
    73  
    74  // GetPKIidOfCert returns the PKI-ID of a peer's identity
    75  func (*naiveCryptoService) GetPKIidOfCert(peerIdentity api.PeerIdentityType) common.PKIidType {
    76  	return common.PKIidType(peerIdentity)
    77  }
    78  
    79  // VerifyBlock returns nil if the block is properly signed,
    80  // else returns error
    81  func (*naiveCryptoService) VerifyBlock(channelID common.ChannelID, seqNum uint64, signedBlock *cb.Block) error {
    82  	return nil
    83  }
    84  
    85  // VerifyByChannel verifies a peer's signature on a message in the context
    86  // of a specific channel
    87  func (*naiveCryptoService) VerifyByChannel(_ common.ChannelID, _ api.PeerIdentityType, _, _ []byte) error {
    88  	return nil
    89  }
    90  
    91  // Sign signs msg with this peer's signing key and outputs
    92  // the signature if no error occurred.
    93  func (*naiveCryptoService) Sign(msg []byte) ([]byte, error) {
    94  	return msg, nil
    95  }
    96  
    97  // Verify checks that signature is a valid signature of message under a peer's verification key.
    98  // If the verification succeeded, Verify returns nil meaning no error occurred.
    99  // If peerCert is nil, then the signature is verified against this peer's verification key.
   100  func (*naiveCryptoService) Verify(peerIdentity api.PeerIdentityType, signature, message []byte) error {
   101  	equal := bytes.Equal(signature, message)
   102  	if !equal {
   103  		return fmt.Errorf("Wrong certificate")
   104  	}
   105  	return nil
   106  }
   107  
   108  func TestPut(t *testing.T) {
   109  	idStore := NewIdentityMapper(msgCryptoService, dummyID, noopPurgeTrigger, msgCryptoService)
   110  	identity := []byte("yacovm")
   111  	identity2 := []byte("not-yacovm")
   112  	identity3 := []byte("invalidIdentity")
   113  	msgCryptoService.revokedIdentities[string(identity3)] = struct{}{}
   114  	pkiID := msgCryptoService.GetPKIidOfCert(api.PeerIdentityType(identity))
   115  	pkiID2 := msgCryptoService.GetPKIidOfCert(api.PeerIdentityType(identity2))
   116  	pkiID3 := msgCryptoService.GetPKIidOfCert(api.PeerIdentityType(identity3))
   117  	assert.NoError(t, idStore.Put(pkiID, identity))
   118  	assert.NoError(t, idStore.Put(pkiID, identity))
   119  	assert.Error(t, idStore.Put(nil, identity))
   120  	assert.Error(t, idStore.Put(pkiID2, nil))
   121  	assert.Error(t, idStore.Put(pkiID2, identity))
   122  	assert.Error(t, idStore.Put(pkiID, identity2))
   123  	assert.Error(t, idStore.Put(pkiID3, identity3))
   124  }
   125  
   126  func TestGet(t *testing.T) {
   127  	idStore := NewIdentityMapper(msgCryptoService, dummyID, noopPurgeTrigger, msgCryptoService)
   128  	identity := []byte("yacovm")
   129  	identity2 := []byte("not-yacovm")
   130  	pkiID := msgCryptoService.GetPKIidOfCert(api.PeerIdentityType(identity))
   131  	pkiID2 := msgCryptoService.GetPKIidOfCert(api.PeerIdentityType(identity2))
   132  	assert.NoError(t, idStore.Put(pkiID, identity))
   133  	cert, err := idStore.Get(pkiID)
   134  	assert.NoError(t, err)
   135  	assert.Equal(t, api.PeerIdentityType(identity), cert)
   136  	cert, err = idStore.Get(pkiID2)
   137  	assert.Nil(t, cert)
   138  	assert.Error(t, err)
   139  }
   140  
   141  func TestVerify(t *testing.T) {
   142  	idStore := NewIdentityMapper(msgCryptoService, dummyID, noopPurgeTrigger, msgCryptoService)
   143  	identity := []byte("yacovm")
   144  	identity2 := []byte("not-yacovm")
   145  	pkiID := msgCryptoService.GetPKIidOfCert(api.PeerIdentityType(identity))
   146  	pkiID2 := msgCryptoService.GetPKIidOfCert(api.PeerIdentityType(identity2))
   147  	idStore.Put(pkiID, api.PeerIdentityType(identity))
   148  	signed, err := idStore.Sign([]byte("bla bla"))
   149  	assert.NoError(t, err)
   150  	assert.NoError(t, idStore.Verify(pkiID, signed, []byte("bla bla")))
   151  	assert.Error(t, idStore.Verify(pkiID2, signed, []byte("bla bla")))
   152  }
   153  
   154  func TestListInvalidIdentities(t *testing.T) {
   155  	deletedIdentities := make(chan string, 1)
   156  	assertDeletedIdentity := func(expected string) {
   157  		select {
   158  		case <-time.After(time.Second * 10):
   159  			t.Fatalf("Didn't detect a deleted identity, expected %s to be deleted", expected)
   160  		case actual := <-deletedIdentities:
   161  			assert.Equal(t, expected, actual)
   162  		}
   163  	}
   164  	// set the time-based expiration time limit to something small
   165  	SetIdentityUsageThreshold(time.Millisecond * 500)
   166  	assert.Equal(t, time.Millisecond*500, GetIdentityUsageThreshold())
   167  	selfPKIID := msgCryptoService.GetPKIidOfCert(dummyID)
   168  	idStore := NewIdentityMapper(msgCryptoService, dummyID, func(_ common.PKIidType, identity api.PeerIdentityType) {
   169  		deletedIdentities <- string(identity)
   170  	}, msgCryptoService)
   171  	identity := []byte("yacovm")
   172  	// Test for a revoked identity
   173  	pkiID := msgCryptoService.GetPKIidOfCert(api.PeerIdentityType(identity))
   174  	assert.NoError(t, idStore.Put(pkiID, api.PeerIdentityType(identity)))
   175  	cert, err := idStore.Get(pkiID)
   176  	assert.NoError(t, err)
   177  	assert.NotNil(t, cert)
   178  	// Revoke the certificate
   179  	msgCryptoService.revokedIdentities[string(pkiID)] = struct{}{}
   180  	idStore.SuspectPeers(func(_ api.PeerIdentityType) bool {
   181  		return true
   182  	})
   183  	// Make sure it is not found anymore
   184  	cert, err = idStore.Get(pkiID)
   185  	assert.Error(t, err)
   186  	assert.Nil(t, cert)
   187  	assertDeletedIdentity("yacovm")
   188  
   189  	// Clean the MCS revocation mock
   190  	msgCryptoService.revokedIdentities = map[string]struct{}{}
   191  	// Now, test for a certificate that has not been used
   192  	// for a long time
   193  	// Add back the identity
   194  	pkiID = msgCryptoService.GetPKIidOfCert(api.PeerIdentityType(identity))
   195  	assert.NoError(t, idStore.Put(pkiID, api.PeerIdentityType(identity)))
   196  	// Check it exists in the meantime
   197  	cert, err = idStore.Get(pkiID)
   198  	assert.NoError(t, err)
   199  	assert.NotNil(t, cert)
   200  	time.Sleep(time.Second * 3)
   201  	// Make sure it has expired
   202  	cert, err = idStore.Get(pkiID)
   203  	assert.Error(t, err)
   204  	assert.Nil(t, cert)
   205  	assertDeletedIdentity("yacovm")
   206  	// Make sure our own identity hasn't been expired
   207  	_, err = idStore.Get(selfPKIID)
   208  	assert.NoError(t, err)
   209  
   210  	// Now test that an identity that is frequently used doesn't expire
   211  	// Add back the identity
   212  	pkiID = msgCryptoService.GetPKIidOfCert(api.PeerIdentityType(identity))
   213  	assert.NoError(t, idStore.Put(pkiID, api.PeerIdentityType(identity)))
   214  	stopChan := make(chan struct{})
   215  	go func() {
   216  		for {
   217  			select {
   218  			case <-stopChan:
   219  				return
   220  			case <-time.After(time.Millisecond * 10):
   221  				idStore.Get(pkiID)
   222  			}
   223  		}
   224  	}()
   225  	time.Sleep(time.Second * 3)
   226  	// Ensure it hasn't expired even though time has passed
   227  	cert, err = idStore.Get(pkiID)
   228  	assert.NoError(t, err)
   229  	assert.NotNil(t, cert)
   230  	stopChan <- struct{}{}
   231  	// Stop the identity store - this would make periodical un-usage
   232  	// expiration stop
   233  	idStore.Stop()
   234  	time.Sleep(time.Second * 3)
   235  	// Ensure it hasn't expired even though time has passed
   236  	cert, err = idStore.Get(pkiID)
   237  	assert.NoError(t, err)
   238  	assert.NotNil(t, cert)
   239  }
   240  
   241  func TestExpiration(t *testing.T) {
   242  	deletedIdentities := make(chan string, 1)
   243  	SetIdentityUsageThreshold(time.Second * 500)
   244  	idStore := NewIdentityMapper(msgCryptoService, dummyID, func(_ common.PKIidType, identity api.PeerIdentityType) {
   245  		deletedIdentities <- string(identity)
   246  	}, msgCryptoService)
   247  	assertDeletedIdentity := func(expected string) {
   248  		select {
   249  		case <-time.After(time.Second * 10):
   250  			t.Fatalf("Didn't detect a deleted identity, expected %s to be deleted", expected)
   251  		case actual := <-deletedIdentities:
   252  			assert.Equal(t, expected, actual)
   253  		}
   254  	}
   255  	x509Identity := api.PeerIdentityType("x509Identity")
   256  	expiredX509Identity := api.PeerIdentityType("expiredX509Identity")
   257  	nonX509Identity := api.PeerIdentityType("nonX509Identity")
   258  	notSupportedIdentity := api.PeerIdentityType("notSupportedIdentity")
   259  	x509PkiID := idStore.GetPKIidOfCert(x509Identity)
   260  	expiredX509PkiID := idStore.GetPKIidOfCert(expiredX509Identity)
   261  	nonX509PkiID := idStore.GetPKIidOfCert(nonX509Identity)
   262  	notSupportedPkiID := idStore.GetPKIidOfCert(notSupportedIdentity)
   263  	msgCryptoService.On("Expiration", x509Identity).Return(time.Now().Add(time.Second), nil)
   264  	msgCryptoService.On("Expiration", expiredX509Identity).Return(time.Now().Add(-time.Second), nil)
   265  	msgCryptoService.On("Expiration", nonX509Identity).Return(time.Time{}, nil)
   266  	msgCryptoService.On("Expiration", notSupportedIdentity).Return(time.Time{}, errors.New("no MSP supports given identity"))
   267  	// Add all identities
   268  	err := idStore.Put(x509PkiID, x509Identity)
   269  	assert.NoError(t, err)
   270  	err = idStore.Put(expiredX509PkiID, expiredX509Identity)
   271  	assert.Equal(t, "identity expired", err.Error())
   272  	err = idStore.Put(nonX509PkiID, nonX509Identity)
   273  	assert.NoError(t, err)
   274  	err = idStore.Put(notSupportedPkiID, notSupportedIdentity)
   275  	assert.Error(t, err)
   276  	assert.Contains(t, err.Error(), "no MSP supports given identity")
   277  
   278  	// Make sure the x509 cert and the non x509 cert exist in the store
   279  	returnedIdentity, err := idStore.Get(x509PkiID)
   280  	assert.NoError(t, err)
   281  	assert.NotEmpty(t, returnedIdentity)
   282  
   283  	returnedIdentity, err = idStore.Get(nonX509PkiID)
   284  	assert.NoError(t, err)
   285  	assert.NotEmpty(t, returnedIdentity)
   286  
   287  	// Wait for the x509 identity to expire
   288  	time.Sleep(time.Second * 3)
   289  
   290  	// Ensure only the non x509 identity exists now
   291  	returnedIdentity, err = idStore.Get(x509PkiID)
   292  	assert.Error(t, err)
   293  	assert.Contains(t, err.Error(), "PKIID wasn't found")
   294  	assert.Empty(t, returnedIdentity)
   295  	assertDeletedIdentity("x509Identity")
   296  
   297  	returnedIdentity, err = idStore.Get(nonX509PkiID)
   298  	assert.NoError(t, err)
   299  	assert.NotEmpty(t, returnedIdentity)
   300  
   301  	// Ensure that when it is revoked, an expiration timer isn't cancelled for it
   302  	msgCryptoService.revokedIdentities[string(nonX509PkiID)] = struct{}{}
   303  	idStore.SuspectPeers(func(_ api.PeerIdentityType) bool {
   304  		return true
   305  	})
   306  	assertDeletedIdentity("nonX509Identity")
   307  	msgCryptoService.revokedIdentities = map[string]struct{}{}
   308  }
   309  
   310  func TestExpirationPanic(t *testing.T) {
   311  	identity3 := []byte("invalidIdentity")
   312  	msgCryptoService.revokedIdentities[string(identity3)] = struct{}{}
   313  	assert.Panics(t, func() {
   314  		NewIdentityMapper(msgCryptoService, identity3, noopPurgeTrigger, msgCryptoService)
   315  	})
   316  }
   317  
   318  func TestIdentityInfo(t *testing.T) {
   319  	cs := &naiveCryptoService{}
   320  	alice := api.PeerIdentityType("alicePeer")
   321  	bob := api.PeerIdentityType("bobPeer")
   322  	aliceID := cs.GetPKIidOfCert(alice)
   323  	bobId := cs.GetPKIidOfCert(bob)
   324  	cs.On("OrgByPeerIdentity", dummyID).Return(api.OrgIdentityType("D"))
   325  	cs.On("OrgByPeerIdentity", alice).Return(api.OrgIdentityType("A"))
   326  	cs.On("OrgByPeerIdentity", bob).Return(api.OrgIdentityType("B"))
   327  	cs.On("Expiration", mock.Anything).Return(time.Now().Add(time.Minute), nil)
   328  	idStore := NewIdentityMapper(cs, dummyID, noopPurgeTrigger, cs)
   329  	idStore.Put(aliceID, alice)
   330  	idStore.Put(bobId, bob)
   331  	for org, id := range idStore.IdentityInfo().ByOrg() {
   332  		identity := string(id[0].Identity)
   333  		pkiID := string(id[0].PKIId)
   334  		orgId := string(id[0].Organization)
   335  		assert.Equal(t, org, orgId)
   336  		assert.Equal(t, strings.ToLower(org), string(identity[0]))
   337  		assert.Equal(t, strings.ToLower(org), string(pkiID[0]))
   338  	}
   339  }