github.com/Hnampk/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 }