diff --git a/core/deliverservice/blocksprovider/blocksprovider_test.go b/core/deliverservice/blocksprovider/blocksprovider_test.go index a736820d764..333ab9b5be8 100644 --- a/core/deliverservice/blocksprovider/blocksprovider_test.go +++ b/core/deliverservice/blocksprovider/blocksprovider_test.go @@ -30,6 +30,10 @@ type mockMCS struct { mock.Mock } +func (*mockMCS) Expiration(peerIdentity api.PeerIdentityType) (time.Time, error) { + return time.Now().Add(time.Hour), nil +} + func (*mockMCS) GetPKIidOfCert(peerIdentity api.PeerIdentityType) common2.PKIidType { return common2.PKIidType("pkiID") } diff --git a/core/deliverservice/deliveryclient_test.go b/core/deliverservice/deliveryclient_test.go index 2b585037df0..e25a446b137 100644 --- a/core/deliverservice/deliveryclient_test.go +++ b/core/deliverservice/deliveryclient_test.go @@ -50,6 +50,10 @@ func (mock *mockBlocksDelivererFactory) Create() (blocksprovider.BlocksDeliverer type mockMCS struct { } +func (*mockMCS) Expiration(peerIdentity api.PeerIdentityType) (time.Time, error) { + return time.Now().Add(time.Hour), nil +} + func (*mockMCS) GetPKIidOfCert(peerIdentity api.PeerIdentityType) common.PKIidType { return common.PKIidType("pkiID") } diff --git a/gossip/api/crypto.go b/gossip/api/crypto.go index e9bf076f0f8..1d43be3b2c0 100644 --- a/gossip/api/crypto.go +++ b/gossip/api/crypto.go @@ -7,6 +7,8 @@ SPDX-License-Identifier: Apache-2.0 package api import ( + "time" + "github.com/hyperledger/fabric/gossip/common" "google.golang.org/grpc" ) @@ -47,6 +49,15 @@ type MessageCryptoService interface { // If the identity is invalid, revoked, expired it returns an error. // Else, returns nil ValidateIdentity(peerIdentity PeerIdentityType) error + + // Expiration returns: + // - The time when the identity expires, nil + // In case it can expire + // - A zero value time.Time, nil + // in case it cannot expire + // - A zero value, error in case it cannot be + // determined if the identity can expire or not + Expiration(peerIdentity PeerIdentityType) (time.Time, error) } // PeerIdentityType is the peer's certificate diff --git a/gossip/comm/comm_test.go b/gossip/comm/comm_test.go index 1e7361627d9..253fafae4fc 100644 --- a/gossip/comm/comm_test.go +++ b/gossip/comm/comm_test.go @@ -56,6 +56,10 @@ var ( type naiveSecProvider struct { } +func (*naiveSecProvider) Expiration(peerIdentity api.PeerIdentityType) (time.Time, error) { + return time.Now().Add(time.Hour), nil +} + func (*naiveSecProvider) ValidateIdentity(peerIdentity api.PeerIdentityType) error { return nil } diff --git a/gossip/gossip/channel/channel_test.go b/gossip/gossip/channel/channel_test.go index bdb8f14357c..2bd1ebb378b 100644 --- a/gossip/gossip/channel/channel_test.go +++ b/gossip/gossip/channel/channel_test.go @@ -102,6 +102,10 @@ type cryptoService struct { mock.Mock } +func (cs *cryptoService) Expiration(peerIdentity api.PeerIdentityType) (time.Time, error) { + return time.Now().Add(time.Hour), nil +} + func (cs *cryptoService) GetPKIidOfCert(peerIdentity api.PeerIdentityType) common.PKIidType { panic("Should not be called in this test") } diff --git a/gossip/gossip/gossip_test.go b/gossip/gossip/gossip_test.go index 9b9ac59afc9..6233d099a37 100644 --- a/gossip/gossip/gossip_test.go +++ b/gossip/gossip/gossip_test.go @@ -147,6 +147,10 @@ func (cs *naiveCryptoService) VerifyByChannel(_ common.ChainID, identity api.Pee return errors.New("Forbidden") } +func (*naiveCryptoService) Expiration(peerIdentity api.PeerIdentityType) (time.Time, error) { + return time.Time{}, nil +} + func (cs *naiveCryptoService) ValidateIdentity(peerIdentity api.PeerIdentityType) error { cs.RLock() defer cs.RUnlock() diff --git a/gossip/gossip/orgs_test.go b/gossip/gossip/orgs_test.go index 768bdb2647e..fcf03913edf 100644 --- a/gossip/gossip/orgs_test.go +++ b/gossip/gossip/orgs_test.go @@ -37,6 +37,10 @@ type configurableCryptoService struct { m map[string]api.OrgIdentityType } +func (c *configurableCryptoService) Expiration(peerIdentity api.PeerIdentityType) (time.Time, error) { + return time.Now().Add(time.Hour), nil +} + func (c *configurableCryptoService) putInOrg(port int, org string) { identity := fmt.Sprintf("localhost:%d", port) c.m[identity] = api.OrgIdentityType(org) diff --git a/gossip/identity/identity_test.go b/gossip/identity/identity_test.go index 63b2dccd90c..ee7719ad435 100644 --- a/gossip/identity/identity_test.go +++ b/gossip/identity/identity_test.go @@ -11,7 +11,6 @@ import ( "errors" "fmt" "testing" - "time" "github.com/hyperledger/fabric/gossip/api" @@ -35,6 +34,10 @@ func init() { util.SetupTestLogging() } +func (cs *naiveCryptoService) Expiration(peerIdentity api.PeerIdentityType) (time.Time, error) { + return time.Time{}, nil +} + func (cs *naiveCryptoService) ValidateIdentity(peerIdentity api.PeerIdentityType) error { if _, isRevoked := cs.revokedIdentities[string(cs.GetPKIidOfCert(peerIdentity))]; isRevoked { return errors.New("revoked") diff --git a/gossip/integration/integration_test.go b/gossip/integration/integration_test.go index c187aec83dc..16666c8d822 100644 --- a/gossip/integration/integration_test.go +++ b/gossip/integration/integration_test.go @@ -12,6 +12,8 @@ import ( "strings" "testing" + "time" + "github.com/hyperledger/fabric/core/config" "github.com/hyperledger/fabric/gossip/api" "github.com/hyperledger/fabric/gossip/common" @@ -102,6 +104,10 @@ func (sa *secAdviser) OrgByPeerIdentity(api.PeerIdentityType) api.OrgIdentityTyp type cryptoService struct { } +func (s *cryptoService) Expiration(peerIdentity api.PeerIdentityType) (time.Time, error) { + return time.Now().Add(time.Hour), nil +} + func (s *cryptoService) GetPKIidOfCert(peerIdentity api.PeerIdentityType) common.PKIidType { return common.PKIidType(peerIdentity) } diff --git a/gossip/service/gossip_service_test.go b/gossip/service/gossip_service_test.go index 3210a8e08f3..f758ce2d5b9 100644 --- a/gossip/service/gossip_service_test.go +++ b/gossip/service/gossip_service_test.go @@ -664,6 +664,10 @@ func (*orgCryptoService) Verify(joinChanMsg api.JoinChannelMessage) error { return nil } +func (naiveCryptoService) Expiration(peerIdentity api.PeerIdentityType) (time.Time, error) { + return time.Now().Add(time.Hour), nil +} + // VerifyByChannel verifies a peer's signature on a message in the context // of a specific channel func (*naiveCryptoService) VerifyByChannel(_ gossipCommon.ChainID, _ api.PeerIdentityType, _, _ []byte) error { diff --git a/gossip/state/state_test.go b/gossip/state/state_test.go index 22f7a45844f..896a1d60f6a 100644 --- a/gossip/state/state_test.go +++ b/gossip/state/state_test.go @@ -94,6 +94,10 @@ type cryptoServiceMock struct { acceptor peerIdentityAcceptor } +func (cryptoServiceMock) Expiration(peerIdentity api.PeerIdentityType) (time.Time, error) { + return time.Now().Add(time.Hour), nil +} + // GetPKIidOfCert returns the PKI-ID of a peer's identity func (*cryptoServiceMock) GetPKIidOfCert(peerIdentity api.PeerIdentityType) common.PKIidType { return common.PKIidType(peerIdentity) diff --git a/peer/gossip/mcs.go b/peer/gossip/mcs.go index 58e522b9223..d34f4bb6752 100644 --- a/peer/gossip/mcs.go +++ b/peer/gossip/mcs.go @@ -18,8 +18,8 @@ package gossip import ( "bytes" - "errors" "fmt" + "time" "github.com/hyperledger/fabric/bccsp" "github.com/hyperledger/fabric/bccsp/factory" @@ -33,6 +33,7 @@ import ( "github.com/hyperledger/fabric/msp/mgmt" pcommon "github.com/hyperledger/fabric/protos/common" "github.com/hyperledger/fabric/protos/utils" + "github.com/pkg/errors" ) var mcsLogger = flogging.MustGetLogger("peer/gossip/mcs") @@ -58,7 +59,7 @@ type mspMessageCryptoService struct { // 1. a policies.ChannelPolicyManagerGetter that gives access to the policy manager of a given channel via the Manager method. // 2. an instance of crypto.LocalSigner // 3. an identity deserializer manager -func NewMCS(channelPolicyManagerGetter policies.ChannelPolicyManagerGetter, localSigner crypto.LocalSigner, deserializer mgmt.DeserializersManager) api.MessageCryptoService { +func NewMCS(channelPolicyManagerGetter policies.ChannelPolicyManagerGetter, localSigner crypto.LocalSigner, deserializer mgmt.DeserializersManager) *mspMessageCryptoService { return &mspMessageCryptoService{channelPolicyManagerGetter: channelPolicyManagerGetter, localSigner: localSigner, deserializer: deserializer} } @@ -255,6 +256,15 @@ func (s *mspMessageCryptoService) VerifyByChannel(chainID common.ChainID, peerId ) } +func (s *mspMessageCryptoService) Expiration(peerIdentity api.PeerIdentityType) (time.Time, error) { + id, _, err := s.getValidatedIdentity(peerIdentity) + if err != nil { + return time.Time{}, errors.Wrap(err, "Unable to extract msp.Identity from peer Identity") + } + return id.ExpiresAt(), nil + +} + func (s *mspMessageCryptoService) getValidatedIdentity(peerIdentity api.PeerIdentityType) (msp.Identity, common.ChainID, error) { // Validate arguments if len(peerIdentity) == 0 { diff --git a/peer/gossip/mcs_test.go b/peer/gossip/mcs_test.go index 85639594a7a..231de5033d1 100644 --- a/peer/gossip/mcs_test.go +++ b/peer/gossip/mcs_test.go @@ -19,6 +19,7 @@ package gossip import ( "reflect" "testing" + "time" "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/bccsp" @@ -33,6 +34,7 @@ import ( "github.com/hyperledger/fabric/msp/mgmt" "github.com/hyperledger/fabric/peer/gossip/mocks" "github.com/hyperledger/fabric/protos/common" + pmsp "github.com/hyperledger/fabric/protos/msp" protospeer "github.com/hyperledger/fabric/protos/peer" "github.com/hyperledger/fabric/protos/utils" "github.com/stretchr/testify/assert" @@ -254,3 +256,62 @@ func mockBlock(t *testing.T, channel string, seqNum uint64, localSigner crypto.L return blockRaw, msg } + +func TestExpiration(t *testing.T) { + expirationDate := time.Now().Add(time.Minute) + id1 := &pmsp.SerializedIdentity{ + Mspid: "X509BasedMSP", + IdBytes: []byte("X509BasedIdentity"), + } + + x509IdentityBytes, _ := proto.Marshal(id1) + + id2 := &pmsp.SerializedIdentity{ + Mspid: "nonX509BasedMSP", + IdBytes: []byte("nonX509RawIdentity"), + } + + nonX509IdentityBytes, _ := proto.Marshal(id2) + + deserializersManager := &mocks.DeserializersManager{ + LocalDeserializer: &mocks.IdentityDeserializer{ + Identity: []byte{1, 2, 3}, + Msg: []byte{1, 2, 3}, + }, + ChannelDeserializers: map[string]msp.IdentityDeserializer{ + "X509BasedMSP": &mocks.IdentityDeserializerWithExpiration{ + Expiration: expirationDate, + IdentityDeserializer: &mocks.IdentityDeserializer{ + Identity: x509IdentityBytes, + Msg: []byte("x509IdentityBytes"), + }, + }, + "nonX509BasedMSP": &mocks.IdentityDeserializer{ + Identity: nonX509IdentityBytes, + Msg: []byte("nonX509IdentityBytes"), + }, + }, + } + msgCryptoService := NewMCS( + &mocks.ChannelPolicyManagerGetterWithManager{}, + &mockscrypto.LocalSigner{Identity: []byte("Yacov")}, + deserializersManager, + ) + + // Green path I check the expiration date is as expected + exp, err := msgCryptoService.Expiration(x509IdentityBytes) + assert.NoError(t, err) + assert.Equal(t, expirationDate.Second(), exp.Second()) + + // Green path II - a non-x509 identity has a zero expiration time + exp, err = msgCryptoService.Expiration(nonX509IdentityBytes) + assert.NoError(t, err) + assert.Zero(t, exp) + + // Bad path I - corrupt the x509 identity and make sure error is returned + x509IdentityBytes = append(x509IdentityBytes, 0, 0, 0, 0, 0, 0) + exp, err = msgCryptoService.Expiration(x509IdentityBytes) + assert.Error(t, err) + assert.Contains(t, err.Error(), "No MSP found able to do that") + assert.Zero(t, exp) +} diff --git a/peer/gossip/mocks/mocks.go b/peer/gossip/mocks/mocks.go index 8d4914e3510..7e816a278a7 100644 --- a/peer/gossip/mocks/mocks.go +++ b/peer/gossip/mocks/mocks.go @@ -91,6 +91,20 @@ func (m *DeserializersManager) GetChannelDeserializers() map[string]msp.Identity return m.ChannelDeserializers } +type IdentityDeserializerWithExpiration struct { + *IdentityDeserializer + Expiration time.Time +} + +func (d *IdentityDeserializerWithExpiration) DeserializeIdentity(serializedIdentity []byte) (msp.Identity, error) { + id, err := d.IdentityDeserializer.DeserializeIdentity(serializedIdentity) + if err != nil { + return nil, err + } + id.(*Identity).expirationDate = d.Expiration + return id, nil +} + type IdentityDeserializer struct { Identity []byte Msg []byte @@ -107,11 +121,12 @@ func (d *IdentityDeserializer) DeserializeIdentity(serializedIdentity []byte) (m } type Identity struct { - Msg []byte + expirationDate time.Time + Msg []byte } func (id *Identity) ExpiresAt() time.Time { - return time.Time{} + return id.expirationDate } func (id *Identity) SatisfiesPrincipal(*mspproto.MSPPrincipal) error {