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  }