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 }