github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/gossip/identity/identity.go (about)

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