forked from hyperledger/fabric
-
Notifications
You must be signed in to change notification settings - Fork 13
/
mcs.go
333 lines (278 loc) · 12.7 KB
/
mcs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package gossip
import (
"bytes"
"fmt"
"time"
"github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric/bccsp/factory"
"github.com/hyperledger/fabric/common/crypto"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/gossip/api"
"github.com/hyperledger/fabric/gossip/common"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/msp/mgmt"
pcommon "github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/utils"
"github.com/pkg/errors"
)
var mcsLogger = flogging.MustGetLogger("peer.gossip.mcs")
// MSPMessageCryptoService implements the MessageCryptoService interface
// using the peer MSPs (local and channel-related)
//
// In order for the system to be secure it is vital to have the
// MSPs to be up-to-date. Channels' MSPs are updated via
// configuration transactions distributed by the ordering service.
//
// A similar mechanism needs to be in place to update the local MSP, as well.
// This implementation assumes that these mechanisms are all in place and working.
type MSPMessageCryptoService struct {
channelPolicyManagerGetter policies.ChannelPolicyManagerGetter
localSigner crypto.LocalSigner
deserializer mgmt.DeserializersManager
}
// NewMCS creates a new instance of MSPMessageCryptoService
// that implements MessageCryptoService.
// The method takes in input:
// 1. a policies.ChannelPolicyManagerGetter that gives access to the policy manager of a given channel via the Manager method.
// 2. an instance of crypto.LocalSigner
// 3. an identity deserializer manager
func NewMCS(channelPolicyManagerGetter policies.ChannelPolicyManagerGetter, localSigner crypto.LocalSigner, deserializer mgmt.DeserializersManager) *MSPMessageCryptoService {
return &MSPMessageCryptoService{channelPolicyManagerGetter: channelPolicyManagerGetter, localSigner: localSigner, deserializer: deserializer}
}
// ValidateIdentity validates the identity of a remote peer.
// If the identity is invalid, revoked, expired it returns an error.
// Else, returns nil
func (s *MSPMessageCryptoService) ValidateIdentity(peerIdentity api.PeerIdentityType) error {
// As prescribed by the contract of method,
// below we check only that peerIdentity is not
// invalid, revoked or expired.
_, _, err := s.getValidatedIdentity(peerIdentity)
return err
}
// GetPKIidOfCert returns the PKI-ID of a peer's identity
// If any error occurs, the method return nil
// The PKid of a peer is computed as the SHA2-256 of peerIdentity which
// is supposed to be the serialized version of MSP identity.
// This method does not validate peerIdentity.
// This validation is supposed to be done appropriately during the execution flow.
func (s *MSPMessageCryptoService) GetPKIidOfCert(peerIdentity api.PeerIdentityType) common.PKIidType {
// Validate arguments
if len(peerIdentity) == 0 {
mcsLogger.Error("Invalid Peer Identity. It must be different from nil.")
return nil
}
sid, err := s.deserializer.Deserialize(peerIdentity)
if err != nil {
mcsLogger.Errorf("Failed getting validated identity from peer identity [% x]: [%s]", peerIdentity, err)
return nil
}
// concatenate msp-id and idbytes
// idbytes is the low-level representation of an identity.
// it is supposed to be already in its minimal representation
mspIdRaw := []byte(sid.Mspid)
raw := append(mspIdRaw, sid.IdBytes...)
// Hash
digest, err := factory.GetDefault().Hash(raw, &bccsp.SHA256Opts{})
if err != nil {
mcsLogger.Errorf("Failed computing digest of serialized identity [% x]: [%s]", peerIdentity, err)
return nil
}
return digest
}
// VerifyBlock returns nil if the block is properly signed, and the claimed seqNum is the
// sequence number that the block's header contains.
// else returns error
func (s *MSPMessageCryptoService) VerifyBlock(chainID common.ChainID, seqNum uint64, signedBlock []byte) error {
// - Convert signedBlock to common.Block.
block, err := utils.GetBlockFromBlockBytes(signedBlock)
if err != nil {
return fmt.Errorf("Failed unmarshalling block bytes on channel [%s]: [%s]", chainID, err)
}
if block.Header == nil {
return fmt.Errorf("Invalid Block on channel [%s]. Header must be different from nil.", chainID)
}
blockSeqNum := block.Header.Number
if seqNum != blockSeqNum {
return fmt.Errorf("Claimed seqNum is [%d] but actual seqNum inside block is [%d]", seqNum, blockSeqNum)
}
// - Extract channelID and compare with chainID
channelID, err := utils.GetChainIDFromBlock(block)
if err != nil {
return fmt.Errorf("Failed getting channel id from block with id [%d] on channel [%s]: [%s]", block.Header.Number, chainID, err)
}
if channelID != string(chainID) {
return fmt.Errorf("Invalid block's channel id. Expected [%s]. Given [%s]", chainID, channelID)
}
// - Unmarshal medatada
if block.Metadata == nil || len(block.Metadata.Metadata) == 0 {
return fmt.Errorf("Block with id [%d] on channel [%s] does not have metadata. Block not valid.", block.Header.Number, chainID)
}
metadata, err := utils.GetMetadataFromBlock(block, pcommon.BlockMetadataIndex_SIGNATURES)
if err != nil {
return fmt.Errorf("Failed unmarshalling medatata for signatures [%s]", err)
}
// - Verify that Header.DataHash is equal to the hash of block.Data
// This is to ensure that the header is consistent with the data carried by this block
if !bytes.Equal(block.Data.Hash(), block.Header.DataHash) {
return fmt.Errorf("Header.DataHash is different from Hash(block.Data) for block with id [%d] on channel [%s]", block.Header.Number, chainID)
}
// - Get Policy for block validation
// Get the policy manager for channelID
cpm, ok := s.channelPolicyManagerGetter.Manager(channelID)
if cpm == nil {
return fmt.Errorf("Could not acquire policy manager for channel %s", channelID)
}
// ok is true if it was the manager requested, or false if it is the default manager
mcsLogger.Debugf("Got policy manager for channel [%s] with flag [%t]", channelID, ok)
// Get block validation policy
policy, ok := cpm.GetPolicy(policies.BlockValidation)
// ok is true if it was the policy requested, or false if it is the default policy
mcsLogger.Debugf("Got block validation policy for channel [%s] with flag [%t]", channelID, ok)
// - Prepare SignedData
signatureSet := []*pcommon.SignedData{}
for _, metadataSignature := range metadata.Signatures {
shdr, err := utils.GetSignatureHeader(metadataSignature.SignatureHeader)
if err != nil {
return fmt.Errorf("Failed unmarshalling signature header for block with id [%d] on channel [%s]: [%s]", block.Header.Number, chainID, err)
}
signatureSet = append(
signatureSet,
&pcommon.SignedData{
Identity: shdr.Creator,
Data: util.ConcatenateBytes(metadata.Value, metadataSignature.SignatureHeader, block.Header.Bytes()),
Signature: metadataSignature.Signature,
},
)
}
// - Evaluate policy
return policy.Evaluate(signatureSet)
}
// Sign signs msg with this peer's signing key and outputs
// the signature if no error occurred.
func (s *MSPMessageCryptoService) Sign(msg []byte) ([]byte, error) {
return s.localSigner.Sign(msg)
}
// Verify checks that signature is a valid signature of message under a peer's verification key.
// If the verification succeeded, Verify returns nil meaning no error occurred.
// If peerIdentity is nil, then the verification fails.
func (s *MSPMessageCryptoService) Verify(peerIdentity api.PeerIdentityType, signature, message []byte) error {
identity, chainID, err := s.getValidatedIdentity(peerIdentity)
if err != nil {
mcsLogger.Errorf("Failed getting validated identity from peer identity [%s]", err)
return err
}
if len(chainID) == 0 {
// At this stage, this means that peerIdentity
// belongs to this peer's LocalMSP.
// The signature is validated directly
return identity.Verify(message, signature)
}
// At this stage, the signature must be validated
// against the reader policy of the channel
// identified by chainID
return s.VerifyByChannel(chainID, peerIdentity, signature, message)
}
// VerifyByChannel checks that signature is a valid signature of message
// under a peer's verification key, but also in the context of a specific channel.
// If the verification succeeded, Verify returns nil meaning no error occurred.
// If peerIdentity is nil, then the verification fails.
func (s *MSPMessageCryptoService) VerifyByChannel(chainID common.ChainID, peerIdentity api.PeerIdentityType, signature, message []byte) error {
// Validate arguments
if len(peerIdentity) == 0 {
return errors.New("Invalid Peer Identity. It must be different from nil.")
}
// Get the policy manager for channel chainID
cpm, flag := s.channelPolicyManagerGetter.Manager(string(chainID))
if cpm == nil {
return fmt.Errorf("Could not acquire policy manager for channel %s", string(chainID))
}
mcsLogger.Debugf("Got policy manager for channel [%s] with flag [%t]", string(chainID), flag)
// Get channel reader policy
policy, flag := cpm.GetPolicy(policies.ChannelApplicationReaders)
mcsLogger.Debugf("Got reader policy for channel [%s] with flag [%t]", string(chainID), flag)
return policy.Evaluate(
[]*pcommon.SignedData{{
Data: message,
Identity: []byte(peerIdentity),
Signature: signature,
}},
)
}
func (s *MSPMessageCryptoService) Expiration(peerIdentity api.PeerIdentityType) (time.Time, error) {
id, _, err := s.getValidatedIdentity(peerIdentity)
if err != nil {
return time.Time{}, errors.Wrap(err, "Unable to extract msp.Identity from peer Identity")
}
return id.ExpiresAt(), nil
}
func (s *MSPMessageCryptoService) getValidatedIdentity(peerIdentity api.PeerIdentityType) (msp.Identity, common.ChainID, error) {
// Validate arguments
if len(peerIdentity) == 0 {
return nil, nil, errors.New("Invalid Peer Identity. It must be different from nil.")
}
sId, err := s.deserializer.Deserialize(peerIdentity)
if err != nil {
mcsLogger.Error("failed deserializing identity", err)
return nil, nil, err
}
// Notice that peerIdentity is assumed to be the serialization of an identity.
// So, first step is the identity deserialization and then verify it.
// First check against the local MSP.
// If the peerIdentity is in the same organization of this node then
// the local MSP is required to take the final decision on the validity
// of the signature.
lDes := s.deserializer.GetLocalDeserializer()
identity, err := lDes.DeserializeIdentity([]byte(peerIdentity))
if err == nil {
// No error means that the local MSP successfully deserialized the identity.
// We now check additional properties.
if err := lDes.IsWellFormed(sId); err != nil {
return nil, nil, errors.Wrap(err, "identity is not well formed")
}
// TODO: The following check will be replaced by a check on the organizational units
// when we allow the gossip network to have organization unit (MSP subdivisions)
// scoped messages.
// The following check is consistent with the SecurityAdvisor#OrgByPeerIdentity
// implementation.
// TODO: Notice that the following check saves us from the fact
// that DeserializeIdentity does not yet enforce MSP-IDs consistency.
// This check can be removed once DeserializeIdentity will be fixed.
if identity.GetMSPIdentifier() == s.deserializer.GetLocalMSPIdentifier() {
// Check identity validity
// Notice that at this stage we don't have to check the identity
// against any channel's policies.
// This will be done by the caller function, if needed.
return identity, nil, identity.Validate()
}
}
// Check against managers
for chainID, mspManager := range s.deserializer.GetChannelDeserializers() {
// Deserialize identity
identity, err := mspManager.DeserializeIdentity([]byte(peerIdentity))
if err != nil {
mcsLogger.Debugf("Failed deserialization identity [% x] on [%s]: [%s]", peerIdentity, chainID, err)
continue
}
// We managed deserializing the identity with this MSP manager. Now we check if it's well formed.
if err := mspManager.IsWellFormed(sId); err != nil {
return nil, nil, errors.Wrap(err, "identity is not well formed")
}
// Check identity validity
// Notice that at this stage we don't have to check the identity
// against any channel's policies.
// This will be done by the caller function, if needed.
if err := identity.Validate(); err != nil {
mcsLogger.Debugf("Failed validating identity [% x] on [%s]: [%s]", peerIdentity, chainID, err)
continue
}
mcsLogger.Debugf("Validation succeeded [% x] on [%s]", peerIdentity, chainID)
return identity, common.ChainID(chainID), nil
}
return nil, nil, fmt.Errorf("Peer Identity [% x] cannot be validated. No MSP found able to do that.", peerIdentity)
}