github.com/true-sqn/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 }