github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/orderer/consensus/etcdraft/util_test.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package etcdraft 8 9 import ( 10 "crypto/rand" 11 "crypto/x509" 12 "encoding/base64" 13 "encoding/pem" 14 "fmt" 15 "io/ioutil" 16 "path/filepath" 17 "testing" 18 "time" 19 20 "github.com/golang/protobuf/proto" 21 "github.com/hechain20/hechain/bccsp/sw" 22 "github.com/hechain20/hechain/common/crypto/tlsgen" 23 "github.com/hechain20/hechain/common/flogging" 24 "github.com/hechain20/hechain/orderer/common/cluster" 25 "github.com/hechain20/hechain/protoutil" 26 "github.com/hyperledger/fabric-protos-go/common" 27 etcdraftproto "github.com/hyperledger/fabric-protos-go/orderer/etcdraft" 28 "github.com/stretchr/testify/assert" 29 "github.com/stretchr/testify/require" 30 ) 31 32 func TestIsConsenterOfChannel(t *testing.T) { 33 certInsideConfigBlock, err := base64.StdEncoding.DecodeString("LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNmekNDQWlhZ0F3SUJBZ0l" + 34 "SQUo4bjFLYTVzS1ZaTXRMTHJ1dldERDB3Q2dZSUtvWkl6ajBFQXdJd2JERUwKTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnVENrTmhiR" + 35 "2xtYjNKdWFXRXhGakFVQmdOVkJBY1REVk5oYmlCRwpjbUZ1WTJselkyOHhGREFTQmdOVkJBb1RDMlY0WVcxd2JHVXVZMjl0TVJvd0dBWUR" + 36 "WUVFERXhGMGJITmpZUzVsCmVHRnRjR3hsTG1OdmJUQWVGdzB4T0RFeE1EWXdPVFE1TURCYUZ3MHlPREV4TURNd09UUTVNREJhTUZreEN6QU" + 37 "oKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saE1SWXdGQVlEVlFRSEV3MVRZVzRnUm5KaApibU5wYzJOdk1SMHdH" + 38 "d1lEVlFRREV4UnZjbVJsY21WeU1TNWxlR0Z0Y0d4bExtTnZiVEJaTUJNR0J5cUdTTTQ5CkFnRUdDQ3FHU000OUF3RUhBMElBQkRUVlFZc0" + 39 "ZKZWxUcFZDMDFsek5DSkx6OENRMFFGVDBvN1BmSnBwSkl2SXgKUCtRVjQvRGRCSnRqQ0cvcGsvMGFxZXRpSjhZRUFMYmMrOUhmWnExN2tJ" + 40 "Q2pnYnN3Z2Jnd0RnWURWUjBQQVFILwpCQVFEQWdXZ01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUJCZ2dyQmdFRkJRY0RBakFNQmdOV" + 41 "khSTUJBZjhFCkFqQUFNQ3NHQTFVZEl3UWtNQ0tBSUVBOHFrSVJRTVBuWkxBR2g0TXZla2gzZFpHTmNxcEhZZWlXdzE3Rmw0ZlMKTUV3R0" + 42 "ExVWRFUVJGTUVPQ0ZHOXlaR1Z5WlhJeExtVjRZVzF3YkdVdVkyOXRnZ2h2Y21SbGNtVnlNWUlKYkc5agpZV3hvYjNOMGh3Ui9BQUFCaHh" + 43 "BQUFBQUFBQUFBQUFBQUFBQUFBQUFCTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDCklFckJZRFVzV0JwOHB0ZVFSaTZyNjNVelhJQi81Sn" + 44 "YxK0RlTkRIUHc3aDljQWlCakYrM3V5TzBvMEdRclB4MEUKUWptYlI5T3BVREN2LzlEUkNXWU9GZitkVlE9PQotLS0tLUVORCBDRVJUSU" + 45 "ZJQ0FURS0tLS0tCg==") 46 require.NoError(t, err) 47 48 ca, err := tlsgen.NewCA() 49 require.NoError(t, err) 50 51 kp, err := ca.NewClientCertKeyPair() 52 require.NoError(t, err) 53 54 validBlock := func() *common.Block { 55 b, err := ioutil.ReadFile(filepath.Join("testdata", "etcdraftgenesis.block")) 56 require.NoError(t, err) 57 block := &common.Block{} 58 err = proto.Unmarshal(b, block) 59 require.NoError(t, err) 60 return block 61 } 62 for _, testCase := range []struct { 63 name string 64 expectedError string 65 configBlock *common.Block 66 certificate []byte 67 }{ 68 { 69 name: "nil block", 70 expectedError: "nil block or nil header", 71 }, 72 { 73 name: "nil header", 74 expectedError: "nil block or nil header", 75 configBlock: &common.Block{}, 76 }, 77 { 78 name: "no block data", 79 expectedError: "block data is nil", 80 configBlock: &common.Block{Header: &common.BlockHeader{}}, 81 }, 82 { 83 name: "invalid envelope inside block", 84 expectedError: "failed to unmarshal payload from envelope:" + 85 " error unmarshalling Payload: proto: common.Payload: illegal tag 0 (wire type 1)", 86 configBlock: &common.Block{ 87 Header: &common.BlockHeader{}, 88 Data: &common.BlockData{ 89 Data: [][]byte{protoutil.MarshalOrPanic(&common.Envelope{ 90 Payload: []byte{1, 2, 3}, 91 })}, 92 }, 93 }, 94 }, 95 { 96 name: "valid config block with cert mismatch", 97 configBlock: validBlock(), 98 certificate: kp.Cert, 99 expectedError: cluster.ErrNotInChannel.Error(), 100 }, 101 { 102 name: "valid config block with matching cert", 103 configBlock: validBlock(), 104 certificate: certInsideConfigBlock, 105 }, 106 } { 107 t.Run(testCase.name, func(t *testing.T) { 108 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 109 require.NoError(t, err) 110 111 consenterCertificate := &ConsenterCertificate{ 112 Logger: flogging.MustGetLogger("test"), 113 ConsenterCertificate: testCase.certificate, 114 CryptoProvider: cryptoProvider, 115 } 116 err = consenterCertificate.IsConsenterOfChannel(testCase.configBlock) 117 if testCase.expectedError != "" { 118 require.EqualError(t, err, testCase.expectedError) 119 } else { 120 require.NoError(t, err) 121 } 122 }) 123 } 124 } 125 126 func TestVerifyConfigMetadata(t *testing.T) { 127 tlsCA, err := tlsgen.NewCA() 128 require.NoError(t, err, "failed to create CA") 129 130 caRootCert, err := parseCertificateFromBytes(tlsCA.CertBytes()) 131 require.NoError(t, err, "failed to parse CA certificate") 132 133 serverPair, err := tlsCA.NewServerCertKeyPair("localhost") 134 require.NoError(t, err, "failed to create server key pair") 135 136 clientPair, err := tlsCA.NewClientCertKeyPair() 137 require.NoError(t, err, "failed to create client key pair") 138 139 unknownTlsCA, err := tlsgen.NewCA() 140 require.NoError(t, err, "failed to create unknown CA") 141 142 unknownServerPair, err := unknownTlsCA.NewServerCertKeyPair("unknownhost") 143 require.NoError(t, err, "failed to create unknown server key pair") 144 145 unknownServerCert, err := parseCertificateFromBytes(unknownServerPair.Cert) 146 require.NoError(t, err, "failed to parse unknown server certificate") 147 148 unknownClientPair, err := unknownTlsCA.NewClientCertKeyPair() 149 require.NoError(t, err, "failed to create unknown client key pair") 150 151 unknownClientCert, err := parseCertificateFromBytes(unknownClientPair.Cert) 152 require.NoError(t, err, "failed to parse unknown client certificate") 153 154 validOptions := &etcdraftproto.Options{ 155 TickInterval: "500ms", 156 ElectionTick: 10, 157 HeartbeatTick: 1, 158 MaxInflightBlocks: 5, 159 SnapshotIntervalSize: 20 * 1024 * 1024, // 20 MB 160 } 161 singleConsenter := &etcdraftproto.Consenter{ 162 Host: "host1", 163 Port: 10001, 164 ClientTlsCert: clientPair.Cert, 165 ServerTlsCert: serverPair.Cert, 166 } 167 168 rootCertPool := x509.NewCertPool() 169 rootCertPool.AddCert(caRootCert) 170 goodVerifyingOpts := x509.VerifyOptions{ 171 Roots: rootCertPool, 172 KeyUsages: []x509.ExtKeyUsage{ 173 x509.ExtKeyUsageClientAuth, 174 x509.ExtKeyUsageServerAuth, 175 }, 176 } 177 178 // valid metadata should give nil error 179 goodMetadata := &etcdraftproto.ConfigMetadata{ 180 Options: validOptions, 181 Consenters: []*etcdraftproto.Consenter{ 182 singleConsenter, 183 }, 184 } 185 assert.Nil(t, VerifyConfigMetadata(goodMetadata, goodVerifyingOpts)) 186 187 // test variety of bad metadata 188 for _, testCase := range []struct { 189 description string 190 metadata *etcdraftproto.ConfigMetadata 191 verifyOpts x509.VerifyOptions 192 errRegex string 193 }{ 194 { 195 description: "nil metadata", 196 metadata: nil, 197 errRegex: "nil Raft config metadata", 198 verifyOpts: goodVerifyingOpts, 199 }, 200 { 201 description: "nil options", 202 metadata: &etcdraftproto.ConfigMetadata{}, 203 verifyOpts: goodVerifyingOpts, 204 errRegex: "nil Raft config metadata options", 205 }, 206 { 207 description: "HeartbeatTick is 0", 208 metadata: &etcdraftproto.ConfigMetadata{ 209 Options: &etcdraftproto.Options{ 210 HeartbeatTick: 0, 211 }, 212 }, 213 verifyOpts: goodVerifyingOpts, 214 errRegex: "none of HeartbeatTick .* can be zero", 215 }, 216 { 217 description: "ElectionTick is 0", 218 metadata: &etcdraftproto.ConfigMetadata{ 219 Options: &etcdraftproto.Options{ 220 HeartbeatTick: validOptions.HeartbeatTick, 221 ElectionTick: 0, 222 }, 223 }, 224 verifyOpts: goodVerifyingOpts, 225 errRegex: "none of .* ElectionTick .* can be zero", 226 }, 227 { 228 description: "MaxInflightBlocks is 0", 229 metadata: &etcdraftproto.ConfigMetadata{ 230 Options: &etcdraftproto.Options{ 231 HeartbeatTick: validOptions.HeartbeatTick, 232 ElectionTick: validOptions.ElectionTick, 233 MaxInflightBlocks: 0, 234 }, 235 }, 236 verifyOpts: goodVerifyingOpts, 237 errRegex: "none of .* MaxInflightBlocks .* can be zero", 238 }, 239 { 240 description: "ElectionTick is less than HeartbeatTick", 241 metadata: &etcdraftproto.ConfigMetadata{ 242 Options: &etcdraftproto.Options{ 243 HeartbeatTick: 10, 244 ElectionTick: 1, 245 MaxInflightBlocks: validOptions.MaxInflightBlocks, 246 }, 247 }, 248 verifyOpts: goodVerifyingOpts, 249 errRegex: "ElectionTick .* must be greater than HeartbeatTick", 250 }, 251 { 252 description: "TickInterval is not parsable", 253 metadata: &etcdraftproto.ConfigMetadata{ 254 Options: &etcdraftproto.Options{ 255 HeartbeatTick: validOptions.HeartbeatTick, 256 ElectionTick: validOptions.ElectionTick, 257 MaxInflightBlocks: validOptions.MaxInflightBlocks, 258 TickInterval: "abcd", 259 }, 260 }, 261 verifyOpts: goodVerifyingOpts, 262 errRegex: "failed to parse TickInterval .* to time duration", 263 }, 264 { 265 description: "TickInterval is 0", 266 metadata: &etcdraftproto.ConfigMetadata{ 267 Options: &etcdraftproto.Options{ 268 HeartbeatTick: validOptions.HeartbeatTick, 269 ElectionTick: validOptions.ElectionTick, 270 MaxInflightBlocks: validOptions.MaxInflightBlocks, 271 TickInterval: "0s", 272 }, 273 }, 274 verifyOpts: goodVerifyingOpts, 275 errRegex: "TickInterval cannot be zero", 276 }, 277 { 278 description: "consenter set is empty", 279 metadata: &etcdraftproto.ConfigMetadata{ 280 Options: validOptions, 281 Consenters: []*etcdraftproto.Consenter{}, 282 }, 283 verifyOpts: goodVerifyingOpts, 284 errRegex: "empty consenter set", 285 }, 286 { 287 description: "metadata has nil consenter", 288 metadata: &etcdraftproto.ConfigMetadata{ 289 Options: validOptions, 290 Consenters: []*etcdraftproto.Consenter{ 291 nil, 292 }, 293 }, 294 verifyOpts: goodVerifyingOpts, 295 errRegex: "metadata has nil consenter", 296 }, 297 { 298 description: "consenter has invalid server cert", 299 metadata: &etcdraftproto.ConfigMetadata{ 300 Options: validOptions, 301 Consenters: []*etcdraftproto.Consenter{ 302 { 303 ServerTlsCert: []byte("invalid"), 304 ClientTlsCert: clientPair.Cert, 305 }, 306 }, 307 }, 308 verifyOpts: goodVerifyingOpts, 309 errRegex: "no PEM data found in cert", 310 }, 311 { 312 description: "consenter has invalid client cert", 313 metadata: &etcdraftproto.ConfigMetadata{ 314 Options: validOptions, 315 Consenters: []*etcdraftproto.Consenter{ 316 { 317 ServerTlsCert: serverPair.Cert, 318 ClientTlsCert: []byte("invalid"), 319 }, 320 }, 321 }, 322 verifyOpts: goodVerifyingOpts, 323 errRegex: "no PEM data found in cert", 324 }, 325 { 326 description: "metadata has duplicate consenters", 327 metadata: &etcdraftproto.ConfigMetadata{ 328 Options: validOptions, 329 Consenters: []*etcdraftproto.Consenter{ 330 singleConsenter, 331 singleConsenter, 332 }, 333 }, 334 verifyOpts: goodVerifyingOpts, 335 errRegex: "duplicate consenter", 336 }, 337 { 338 description: "consenter has client cert signed by unknown authority", 339 metadata: &etcdraftproto.ConfigMetadata{ 340 Options: validOptions, 341 Consenters: []*etcdraftproto.Consenter{ 342 { 343 ClientTlsCert: unknownClientPair.Cert, 344 ServerTlsCert: serverPair.Cert, 345 }, 346 }, 347 }, 348 verifyOpts: goodVerifyingOpts, 349 errRegex: fmt.Sprintf("verifying tls client cert with serial number %d: x509: certificate signed by unknown authority", unknownClientCert.SerialNumber), 350 }, 351 { 352 description: "consenter has server cert signed by unknown authority", 353 metadata: &etcdraftproto.ConfigMetadata{ 354 Options: validOptions, 355 Consenters: []*etcdraftproto.Consenter{ 356 { 357 ServerTlsCert: unknownServerPair.Cert, 358 ClientTlsCert: clientPair.Cert, 359 }, 360 }, 361 }, 362 verifyOpts: goodVerifyingOpts, 363 errRegex: fmt.Sprintf("verifying tls server cert with serial number %d: x509: certificate signed by unknown authority", unknownServerCert.SerialNumber), 364 }, 365 } { 366 t.Run(testCase.description, func(t *testing.T) { 367 err := VerifyConfigMetadata(testCase.metadata, testCase.verifyOpts) 368 require.NotNil(t, err, testCase.description) 369 require.Regexp(t, testCase.errRegex, err) 370 }) 371 } 372 373 t.Run("ExpiredCertificate", func(t *testing.T) { 374 clientPair, err := tlsCA.NewClientCertKeyPair() 375 require.NoError(t, err, "failed to create client key pair") 376 377 clientCert := clientPair.TLSCert 378 clientCert.NotAfter = time.Now().Add(-24 * time.Hour) 379 clientCertBytes, err := x509.CreateCertificate(rand.Reader, clientCert, caRootCert, clientPair.Signer.Public(), tlsCA.Signer()) 380 require.NoError(t, err, "failed to create expired certificate") 381 382 clientCert, err = x509.ParseCertificate(clientCertBytes) 383 require.NoError(t, err, "failed to parse expired certificate") 384 385 _, err = clientCert.Verify(goodVerifyingOpts) 386 require.Error(t, err, "expected certificate verification to fail") 387 cie, ok := err.(x509.CertificateInvalidError) 388 require.True(t, ok, "expected an x509.CertificateInvalidError but got %T", err) 389 require.Equal(t, x509.Expired, cie.Reason) 390 391 consenterWithExpiredCerts := &etcdraftproto.Consenter{ 392 Host: "host1", 393 Port: 10001, 394 ClientTlsCert: pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: clientCertBytes}), 395 ServerTlsCert: serverPair.Cert, 396 } 397 398 metadataWithExpiredConsenter := &etcdraftproto.ConfigMetadata{ 399 Options: &etcdraftproto.Options{ 400 TickInterval: "500ms", 401 ElectionTick: 10, 402 HeartbeatTick: 1, 403 MaxInflightBlocks: 5, 404 SnapshotIntervalSize: 20 * 1024 * 1024, // 20 MB 405 }, 406 Consenters: []*etcdraftproto.Consenter{ 407 consenterWithExpiredCerts, 408 }, 409 } 410 411 require.Nil(t, VerifyConfigMetadata(metadataWithExpiredConsenter, goodVerifyingOpts)) 412 }) 413 }