github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/gossip/identity/identity.go (about)

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