github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/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  	"errors"
    12  	"fmt"
    13  	"sync"
    14  	"sync/atomic"
    15  	"time"
    16  
    17  	"github.com/hyperledger/fabric/gossip/api"
    18  	"github.com/hyperledger/fabric/gossip/common"
    19  )
    20  
    21  var (
    22  	// identityUsageThreshold sets the maximum time that an identity
    23  	// can not be used to verify some signature before it will be deleted
    24  	usageThreshold = time.Hour
    25  )
    26  
    27  // Mapper holds mappings between pkiID
    28  // to certificates(identities) of peers
    29  type Mapper interface {
    30  	// Put associates an identity to its given pkiID, and returns an error
    31  	// in case the given pkiID doesn't match the identity
    32  	Put(pkiID common.PKIidType, identity api.PeerIdentityType) error
    33  
    34  	// Get returns the identity of a given pkiID, or error if such an identity
    35  	// isn't found
    36  	Get(pkiID common.PKIidType) (api.PeerIdentityType, error)
    37  
    38  	// Sign signs a message, returns a signed message on success
    39  	// or an error on failure
    40  	Sign(msg []byte) ([]byte, error)
    41  
    42  	// Verify verifies a signed message
    43  	Verify(vkID, signature, message []byte) error
    44  
    45  	// GetPKIidOfCert returns the PKI-ID of a certificate
    46  	GetPKIidOfCert(api.PeerIdentityType) common.PKIidType
    47  
    48  	// ListInvalidIdentities returns a list of PKI-IDs that their corresponding
    49  	// peer identities have been revoked, expired or haven't been used
    50  	// for a long time
    51  	ListInvalidIdentities(isSuspected api.PeerSuspector) []common.PKIidType
    52  }
    53  
    54  // identityMapperImpl is a struct that implements Mapper
    55  type identityMapperImpl struct {
    56  	mcs        api.MessageCryptoService
    57  	pkiID2Cert map[string]*storedIdentity
    58  	sync.RWMutex
    59  	selfPKIID string
    60  }
    61  
    62  // NewIdentityMapper method, all we need is a reference to a MessageCryptoService
    63  func NewIdentityMapper(mcs api.MessageCryptoService, selfIdentity api.PeerIdentityType) Mapper {
    64  	selfPKIID := mcs.GetPKIidOfCert(selfIdentity)
    65  	idMapper := &identityMapperImpl{
    66  		mcs:        mcs,
    67  		pkiID2Cert: make(map[string]*storedIdentity),
    68  		selfPKIID:  string(selfPKIID),
    69  	}
    70  	if err := idMapper.Put(selfPKIID, selfIdentity); err != nil {
    71  		panic(fmt.Errorf("Failed putting our own identity into the identity mapper: %v", err))
    72  	}
    73  	return idMapper
    74  }
    75  
    76  // put associates an identity to its given pkiID, and returns an error
    77  // in case the given pkiID doesn't match the identity
    78  func (is *identityMapperImpl) Put(pkiID common.PKIidType, identity api.PeerIdentityType) error {
    79  	if pkiID == nil {
    80  		return errors.New("PKIID is nil")
    81  	}
    82  	if identity == nil {
    83  		return errors.New("identity is nil")
    84  	}
    85  
    86  	if err := is.mcs.ValidateIdentity(identity); err != nil {
    87  		return err
    88  	}
    89  
    90  	id := is.mcs.GetPKIidOfCert(identity)
    91  	if !bytes.Equal(pkiID, id) {
    92  		return errors.New("identity doesn't match the computed pkiID")
    93  	}
    94  
    95  	is.Lock()
    96  	defer is.Unlock()
    97  	is.pkiID2Cert[string(id)] = newStoredIdentity(identity)
    98  	return nil
    99  }
   100  
   101  // get returns the identity of a given pkiID, or error if such an identity
   102  // isn't found
   103  func (is *identityMapperImpl) Get(pkiID common.PKIidType) (api.PeerIdentityType, error) {
   104  	is.RLock()
   105  	defer is.RUnlock()
   106  	storedIdentity, exists := is.pkiID2Cert[string(pkiID)]
   107  	if !exists {
   108  		return nil, errors.New("PKIID wasn't found")
   109  	}
   110  	return storedIdentity.fetchIdentity(), nil
   111  }
   112  
   113  // Sign signs a message, returns a signed message on success
   114  // or an error on failure
   115  func (is *identityMapperImpl) Sign(msg []byte) ([]byte, error) {
   116  	return is.mcs.Sign(msg)
   117  }
   118  
   119  // Verify verifies a signed message
   120  func (is *identityMapperImpl) Verify(vkID, signature, message []byte) error {
   121  	cert, err := is.Get(vkID)
   122  	if err != nil {
   123  		return err
   124  	}
   125  	return is.mcs.Verify(cert, signature, message)
   126  }
   127  
   128  // GetPKIidOfCert returns the PKI-ID of a certificate
   129  func (is *identityMapperImpl) GetPKIidOfCert(identity api.PeerIdentityType) common.PKIidType {
   130  	return is.mcs.GetPKIidOfCert(identity)
   131  }
   132  
   133  // ListInvalidIdentities returns a list of PKI-IDs that their corresponding
   134  // peer identities have been revoked, expired or haven't been used
   135  // for a long time
   136  func (is *identityMapperImpl) ListInvalidIdentities(isSuspected api.PeerSuspector) []common.PKIidType {
   137  	revokedIds := is.validateIdentities(isSuspected)
   138  	if len(revokedIds) == 0 {
   139  		return nil
   140  	}
   141  	is.Lock()
   142  	defer is.Unlock()
   143  	for _, pkiID := range revokedIds {
   144  		delete(is.pkiID2Cert, string(pkiID))
   145  	}
   146  	return revokedIds
   147  }
   148  
   149  // validateIdentities returns a list of identities that have been revoked, expired or haven't been
   150  // used for a long time
   151  func (is *identityMapperImpl) validateIdentities(isSuspected api.PeerSuspector) []common.PKIidType {
   152  	now := time.Now()
   153  	is.RLock()
   154  	defer is.RUnlock()
   155  	var revokedIds []common.PKIidType
   156  	for pkiID, storedIdentity := range is.pkiID2Cert {
   157  		if pkiID != is.selfPKIID && storedIdentity.fetchLastAccessTime().Add(usageThreshold).Before(now) {
   158  			revokedIds = append(revokedIds, common.PKIidType(pkiID))
   159  			continue
   160  		}
   161  		if !isSuspected(storedIdentity.fetchIdentity()) {
   162  			continue
   163  		}
   164  		if err := is.mcs.ValidateIdentity(storedIdentity.fetchIdentity()); err != nil {
   165  			revokedIds = append(revokedIds, common.PKIidType(pkiID))
   166  		}
   167  	}
   168  	return revokedIds
   169  }
   170  
   171  type storedIdentity struct {
   172  	lastAccessTime int64
   173  	peerIdentity   api.PeerIdentityType
   174  }
   175  
   176  func newStoredIdentity(identity api.PeerIdentityType) *storedIdentity {
   177  	return &storedIdentity{
   178  		lastAccessTime: time.Now().UnixNano(),
   179  		peerIdentity:   identity,
   180  	}
   181  }
   182  
   183  func (si *storedIdentity) fetchIdentity() api.PeerIdentityType {
   184  	atomic.StoreInt64(&si.lastAccessTime, time.Now().UnixNano())
   185  	return si.peerIdentity
   186  }
   187  
   188  func (si *storedIdentity) fetchLastAccessTime() time.Time {
   189  	return time.Unix(0, atomic.LoadInt64(&si.lastAccessTime))
   190  }
   191  
   192  // SetIdentityUsageThreshold sets the usage threshold of identities.
   193  // Identities that are not used at least once during the given time
   194  // are purged
   195  func SetIdentityUsageThreshold(duration time.Duration) {
   196  	usageThreshold = duration
   197  }
   198  
   199  // GetIdentityUsageThreshold returns the usage threshold of identities.
   200  // Identities that are not used at least once during the usage threshold
   201  // duration are purged.
   202  func GetIdentityUsageThreshold() time.Duration {
   203  	return usageThreshold
   204  }