github.com/defanghe/fabric@v2.1.1+incompatible/gossip/identity/identity.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  	"sync"
    12  	"sync/atomic"
    13  	"time"
    14  
    15  	"github.com/hyperledger/fabric/gossip/api"
    16  	"github.com/hyperledger/fabric/gossip/common"
    17  	errors "github.com/pkg/errors"
    18  )
    19  
    20  var (
    21  	// identityUsageThreshold sets the maximum time that an identity
    22  	// can not be used to verify some signature before it will be deleted
    23  	usageThreshold = time.Hour
    24  )
    25  
    26  // Mapper holds mappings between pkiID
    27  // to certificates(identities) of peers
    28  type Mapper interface {
    29  	// Put associates an identity to its given pkiID, and returns an error
    30  	// in case the given pkiID doesn't match the identity
    31  	Put(pkiID common.PKIidType, identity api.PeerIdentityType) error
    32  
    33  	// Get returns the identity of a given pkiID, or error if such an identity
    34  	// isn't found
    35  	Get(pkiID common.PKIidType) (api.PeerIdentityType, error)
    36  
    37  	// Sign signs a message, returns a signed message on success
    38  	// or an error on failure
    39  	Sign(msg []byte) ([]byte, error)
    40  
    41  	// Verify verifies a signed message
    42  	Verify(vkID, signature, message []byte) error
    43  
    44  	// GetPKIidOfCert returns the PKI-ID of a certificate
    45  	GetPKIidOfCert(api.PeerIdentityType) common.PKIidType
    46  
    47  	// SuspectPeers re-validates all peers that match the given predicate
    48  	SuspectPeers(isSuspected api.PeerSuspector)
    49  
    50  	// IdentityInfo returns information known peer identities
    51  	IdentityInfo() api.PeerIdentitySet
    52  
    53  	// Stop stops all background computations of the Mapper
    54  	Stop()
    55  }
    56  
    57  type purgeTrigger func(pkiID common.PKIidType, identity api.PeerIdentityType)
    58  
    59  // identityMapperImpl is a struct that implements Mapper
    60  type identityMapperImpl struct {
    61  	onPurge    purgeTrigger
    62  	mcs        api.MessageCryptoService
    63  	sa         api.SecurityAdvisor
    64  	pkiID2Cert map[string]*storedIdentity
    65  	sync.RWMutex
    66  	stopChan  chan struct{}
    67  	once      sync.Once
    68  	selfPKIID string
    69  }
    70  
    71  // NewIdentityMapper method, all we need is a reference to a MessageCryptoService
    72  func NewIdentityMapper(mcs api.MessageCryptoService, selfIdentity api.PeerIdentityType, onPurge purgeTrigger, sa api.SecurityAdvisor) Mapper {
    73  	selfPKIID := mcs.GetPKIidOfCert(selfIdentity)
    74  	idMapper := &identityMapperImpl{
    75  		onPurge:    onPurge,
    76  		mcs:        mcs,
    77  		pkiID2Cert: make(map[string]*storedIdentity),
    78  		stopChan:   make(chan struct{}),
    79  		selfPKIID:  string(selfPKIID),
    80  		sa:         sa,
    81  	}
    82  	if err := idMapper.Put(selfPKIID, selfIdentity); err != nil {
    83  		panic(errors.Wrap(err, "Failed putting our own identity into the identity mapper"))
    84  	}
    85  	go idMapper.periodicalPurgeUnusedIdentities()
    86  	return idMapper
    87  }
    88  
    89  func (is *identityMapperImpl) periodicalPurgeUnusedIdentities() {
    90  	usageTh := GetIdentityUsageThreshold()
    91  	for {
    92  		select {
    93  		case <-is.stopChan:
    94  			return
    95  		case <-time.After(usageTh / 10):
    96  			is.SuspectPeers(func(_ api.PeerIdentityType) bool {
    97  				return false
    98  			})
    99  		}
   100  	}
   101  }
   102  
   103  // put associates an identity to its given pkiID, and returns an error
   104  // in case the given pkiID doesn't match the identity
   105  func (is *identityMapperImpl) Put(pkiID common.PKIidType, identity api.PeerIdentityType) error {
   106  	if pkiID == nil {
   107  		return errors.New("PKIID is nil")
   108  	}
   109  	if identity == nil {
   110  		return errors.New("identity is nil")
   111  	}
   112  
   113  	expirationDate, err := is.mcs.Expiration(identity)
   114  	if err != nil {
   115  		return errors.Wrap(err, "failed classifying identity")
   116  	}
   117  
   118  	if err := is.mcs.ValidateIdentity(identity); err != nil {
   119  		return err
   120  	}
   121  
   122  	id := is.mcs.GetPKIidOfCert(identity)
   123  	if !bytes.Equal(pkiID, id) {
   124  		return errors.New("identity doesn't match the computed pkiID")
   125  	}
   126  
   127  	is.Lock()
   128  	defer is.Unlock()
   129  	// Check if identity already exists.
   130  	// If so, no need to overwrite it.
   131  	if _, exists := is.pkiID2Cert[string(pkiID)]; exists {
   132  		return nil
   133  	}
   134  
   135  	var expirationTimer *time.Timer
   136  	if !expirationDate.IsZero() {
   137  		if time.Now().After(expirationDate) {
   138  			return errors.New("identity expired")
   139  		}
   140  		// Identity would be wiped out a millisecond after its expiration date
   141  		timeToLive := expirationDate.Add(time.Millisecond).Sub(time.Now())
   142  		expirationTimer = time.AfterFunc(timeToLive, func() {
   143  			is.delete(pkiID, identity)
   144  		})
   145  	}
   146  
   147  	is.pkiID2Cert[string(id)] = newStoredIdentity(pkiID, identity, expirationTimer, is.sa.OrgByPeerIdentity(identity))
   148  	return nil
   149  }
   150  
   151  // get returns the identity of a given pkiID, or error if such an identity
   152  // isn't found
   153  func (is *identityMapperImpl) Get(pkiID common.PKIidType) (api.PeerIdentityType, error) {
   154  	is.RLock()
   155  	defer is.RUnlock()
   156  	storedIdentity, exists := is.pkiID2Cert[string(pkiID)]
   157  	if !exists {
   158  		return nil, errors.New("PKIID wasn't found")
   159  	}
   160  	return storedIdentity.fetchIdentity(), nil
   161  }
   162  
   163  // Sign signs a message, returns a signed message on success
   164  // or an error on failure
   165  func (is *identityMapperImpl) Sign(msg []byte) ([]byte, error) {
   166  	return is.mcs.Sign(msg)
   167  }
   168  
   169  func (is *identityMapperImpl) Stop() {
   170  	is.once.Do(func() {
   171  		close(is.stopChan)
   172  	})
   173  }
   174  
   175  // Verify verifies a signed message
   176  func (is *identityMapperImpl) Verify(vkID, signature, message []byte) error {
   177  	cert, err := is.Get(vkID)
   178  	if err != nil {
   179  		return err
   180  	}
   181  	return is.mcs.Verify(cert, signature, message)
   182  }
   183  
   184  // GetPKIidOfCert returns the PKI-ID of a certificate
   185  func (is *identityMapperImpl) GetPKIidOfCert(identity api.PeerIdentityType) common.PKIidType {
   186  	return is.mcs.GetPKIidOfCert(identity)
   187  }
   188  
   189  // SuspectPeers re-validates all peers that match the given predicate
   190  func (is *identityMapperImpl) SuspectPeers(isSuspected api.PeerSuspector) {
   191  	for _, identity := range is.validateIdentities(isSuspected) {
   192  		identity.cancelExpirationTimer()
   193  		is.delete(identity.pkiID, identity.peerIdentity)
   194  	}
   195  }
   196  
   197  // validateIdentities returns a list of identities that have been revoked, expired or haven't been
   198  // used for a long time
   199  func (is *identityMapperImpl) validateIdentities(isSuspected api.PeerSuspector) []*storedIdentity {
   200  	now := time.Now()
   201  	usageTh := GetIdentityUsageThreshold()
   202  	is.RLock()
   203  	defer is.RUnlock()
   204  	var revokedIdentities []*storedIdentity
   205  	for pkiID, storedIdentity := range is.pkiID2Cert {
   206  		if pkiID != is.selfPKIID && storedIdentity.fetchLastAccessTime().Add(usageTh).Before(now) {
   207  			revokedIdentities = append(revokedIdentities, storedIdentity)
   208  			continue
   209  		}
   210  		if !isSuspected(storedIdentity.peerIdentity) {
   211  			continue
   212  		}
   213  		if err := is.mcs.ValidateIdentity(storedIdentity.fetchIdentity()); err != nil {
   214  			revokedIdentities = append(revokedIdentities, storedIdentity)
   215  		}
   216  	}
   217  	return revokedIdentities
   218  }
   219  
   220  // IdentityInfo returns information known peer identities
   221  func (is *identityMapperImpl) IdentityInfo() api.PeerIdentitySet {
   222  	var res api.PeerIdentitySet
   223  	is.RLock()
   224  	defer is.RUnlock()
   225  	for _, storedIdentity := range is.pkiID2Cert {
   226  		res = append(res, api.PeerIdentityInfo{
   227  			Identity:     storedIdentity.peerIdentity,
   228  			PKIId:        storedIdentity.pkiID,
   229  			Organization: storedIdentity.orgId,
   230  		})
   231  	}
   232  	return res
   233  }
   234  
   235  func (is *identityMapperImpl) delete(pkiID common.PKIidType, identity api.PeerIdentityType) {
   236  	is.Lock()
   237  	defer is.Unlock()
   238  	is.onPurge(pkiID, identity)
   239  	delete(is.pkiID2Cert, string(pkiID))
   240  }
   241  
   242  type storedIdentity struct {
   243  	pkiID           common.PKIidType
   244  	lastAccessTime  int64
   245  	peerIdentity    api.PeerIdentityType
   246  	orgId           api.OrgIdentityType
   247  	expirationTimer *time.Timer
   248  }
   249  
   250  func newStoredIdentity(pkiID common.PKIidType, identity api.PeerIdentityType, expirationTimer *time.Timer, org api.OrgIdentityType) *storedIdentity {
   251  	return &storedIdentity{
   252  		pkiID:           pkiID,
   253  		lastAccessTime:  time.Now().UnixNano(),
   254  		peerIdentity:    identity,
   255  		expirationTimer: expirationTimer,
   256  		orgId:           org,
   257  	}
   258  }
   259  
   260  func (si *storedIdentity) fetchIdentity() api.PeerIdentityType {
   261  	atomic.StoreInt64(&si.lastAccessTime, time.Now().UnixNano())
   262  	return si.peerIdentity
   263  }
   264  
   265  func (si *storedIdentity) fetchLastAccessTime() time.Time {
   266  	return time.Unix(0, atomic.LoadInt64(&si.lastAccessTime))
   267  }
   268  
   269  func (si *storedIdentity) cancelExpirationTimer() {
   270  	if si.expirationTimer == nil {
   271  		return
   272  	}
   273  	si.expirationTimer.Stop()
   274  }
   275  
   276  // SetIdentityUsageThreshold sets the usage threshold of identities.
   277  // Identities that are not used at least once during the given time
   278  // are purged
   279  func SetIdentityUsageThreshold(duration time.Duration) {
   280  	atomic.StoreInt64((*int64)(&usageThreshold), int64(duration))
   281  }
   282  
   283  // GetIdentityUsageThreshold returns the usage threshold of identities.
   284  // Identities that are not used at least once during the usage threshold
   285  // duration are purged.
   286  func GetIdentityUsageThreshold() time.Duration {
   287  	return time.Duration(atomic.LoadInt64((*int64)(&usageThreshold)))
   288  }