From fcc3f95b7517c0ed362f77e9f0e1f20ce8f97bd2 Mon Sep 17 00:00:00 2001 From: Matthew Sykes Date: Wed, 3 Feb 2021 14:58:53 -0500 Subject: [PATCH 1/2] Set SKI, support multi hosts, add Signer to CA Signed-off-by: Matthew Sykes --- common/crypto/tlsgen/ca.go | 20 ++++++++++++++------ common/crypto/tlsgen/ca_test.go | 6 ++++++ common/crypto/tlsgen/key.go | 21 ++++++++++++++++----- common/crypto/tlsgen/key_test.go | 2 +- internal/cryptogen/ca/ca.go | 2 +- 5 files changed, 38 insertions(+), 13 deletions(-) diff --git a/common/crypto/tlsgen/ca.go b/common/crypto/tlsgen/ca.go index 8e9d29fdc22..7ee89d34dc6 100644 --- a/common/crypto/tlsgen/ca.go +++ b/common/crypto/tlsgen/ca.go @@ -40,7 +40,10 @@ type CA interface { // with a given custom SAN. // The certificate is signed by the CA. // Returns nil, error in case of failure - NewServerCertKeyPair(host string) (*CertKeyPair, error) + NewServerCertKeyPair(hosts ...string) (*CertKeyPair, error) + + // Signer returns a crypto.Signer that signs with the CA's private key. + Signer() crypto.Signer } type ca struct { @@ -50,7 +53,7 @@ type ca struct { func NewCA() (CA, error) { c := &ca{} var err error - c.caCert, err = newCertKeyPair(true, false, "", nil, nil) + c.caCert, err = newCertKeyPair(true, false, nil, nil) if err != nil { return nil, err } @@ -60,7 +63,7 @@ func NewCA() (CA, error) { func (c *ca) NewIntermediateCA() (CA, error) { intermediateCA := &ca{} var err error - intermediateCA.caCert, err = newCertKeyPair(true, false, "", c.caCert.Signer, c.caCert.TLSCert) + intermediateCA.caCert, err = newCertKeyPair(true, false, c.caCert.Signer, c.caCert.TLSCert) if err != nil { return nil, err } @@ -76,16 +79,21 @@ func (c *ca) CertBytes() []byte { // or nil, error in case of failure // The certificate is signed by the CA and is used as a client TLS certificate func (c *ca) NewClientCertKeyPair() (*CertKeyPair, error) { - return newCertKeyPair(false, false, "", c.caCert.Signer, c.caCert.TLSCert) + return newCertKeyPair(false, false, c.caCert.Signer, c.caCert.TLSCert) } // newServerCertKeyPair returns a certificate and private key pair and nil, // or nil, error in case of failure // The certificate is signed by the CA and is used as a server TLS certificate -func (c *ca) NewServerCertKeyPair(host string) (*CertKeyPair, error) { - keypair, err := newCertKeyPair(false, true, host, c.caCert.Signer, c.caCert.TLSCert) +func (c *ca) NewServerCertKeyPair(hosts ...string) (*CertKeyPair, error) { + keypair, err := newCertKeyPair(false, true, c.caCert.Signer, c.caCert.TLSCert, hosts...) if err != nil { return nil, err } return keypair, nil } + +// Signer returns a crypto.Signer that signs with the CA's private key. +func (c *ca) Signer() crypto.Signer { + return c.caCert.Signer +} diff --git a/common/crypto/tlsgen/ca_test.go b/common/crypto/tlsgen/ca_test.go index 400bd069fe8..1cdeef93fd1 100644 --- a/common/crypto/tlsgen/ca_test.go +++ b/common/crypto/tlsgen/ca_test.go @@ -82,3 +82,9 @@ func TestTLSCA(t *testing.T) { require.Error(t, err) require.Contains(t, err.Error(), "context deadline exceeded") } + +func TestTLSCASigner(t *testing.T) { + tlsCA, err := NewCA() + require.NoError(t, err) + require.Equal(t, tlsCA.(*ca).caCert.Signer, tlsCA.Signer()) +} diff --git a/common/crypto/tlsgen/key.go b/common/crypto/tlsgen/key.go index a57b0bcd999..ae1ea107bc9 100644 --- a/common/crypto/tlsgen/key.go +++ b/common/crypto/tlsgen/key.go @@ -11,6 +11,7 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" + "crypto/sha256" "crypto/x509" "crypto/x509/pkix" "encoding/pem" @@ -47,7 +48,7 @@ func newCertTemplate() (x509.Certificate, error) { }, nil } -func newCertKeyPair(isCA bool, isServer bool, host string, certSigner crypto.Signer, parent *x509.Certificate) (*CertKeyPair, error) { +func newCertKeyPair(isCA bool, isServer bool, certSigner crypto.Signer, parent *x509.Certificate, hosts ...string) (*CertKeyPair, error) { privateKey, privBytes, err := newPrivKey() if err != nil { return nil, err @@ -74,12 +75,15 @@ func newCertKeyPair(isCA bool, isServer bool, host string, certSigner crypto.Sig if isServer { template.NotAfter = tenYearsFromNow template.ExtKeyUsage = append(template.ExtKeyUsage, x509.ExtKeyUsageServerAuth) - if ip := net.ParseIP(host); ip != nil { - template.IPAddresses = append(template.IPAddresses, ip) - } else { - template.DNSNames = append(template.DNSNames, host) + for _, host := range hosts { + if ip := net.ParseIP(host); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, host) + } } } + template.SubjectKeyId = computeSKI(&privateKey.PublicKey) // If no parent cert, it's a self signed cert if parent == nil || certSigner == nil { parent = &template @@ -111,3 +115,10 @@ func newCertKeyPair(isCA bool, isServer bool, host string, certSigner crypto.Sig func encodePEM(keyType string, data []byte) []byte { return pem.EncodeToMemory(&pem.Block{Type: keyType, Bytes: data}) } + +// RFC 7093, Section 2, Method 4 +func computeSKI(key *ecdsa.PublicKey) []byte { + raw := elliptic.Marshal(key.Curve, key.X, key.Y) + hash := sha256.Sum256(raw) + return hash[:] +} diff --git a/common/crypto/tlsgen/key_test.go b/common/crypto/tlsgen/key_test.go index ccbe5e7a928..d3dc44711b4 100644 --- a/common/crypto/tlsgen/key_test.go +++ b/common/crypto/tlsgen/key_test.go @@ -16,7 +16,7 @@ import ( ) func TestLoadCert(t *testing.T) { - pair, err := newCertKeyPair(false, false, "", nil, nil) + pair, err := newCertKeyPair(false, false, nil, nil) require.NoError(t, err) require.NotNil(t, pair) tlsCertPair, err := tls.X509KeyPair(pair.Cert, pair.Key) diff --git a/internal/cryptogen/ca/ca.go b/internal/cryptogen/ca/ca.go index ca1d3b3c408..240f574c2f1 100644 --- a/internal/cryptogen/ca/ca.go +++ b/internal/cryptogen/ca/ca.go @@ -164,7 +164,7 @@ func (ca *CA) SignCertificate( return cert, nil } -// compute Subject Key Identifier +// compute Subject Key Identifier using RFC 7093, Section 2, Method 4 func computeSKI(privKey *ecdsa.PrivateKey) []byte { // Marshall the public key raw := elliptic.Marshal(privKey.Curve, privKey.PublicKey.X, privKey.PublicKey.Y) From 063cb0e76dea2fc02692fa92452e1acb44756690 Mon Sep 17 00:00:00 2001 From: Matthew Sykes Date: Thu, 4 Feb 2021 09:34:07 -0500 Subject: [PATCH 2/2] Remove etcdraft certificate fixtures Signed-off-by: Matthew Sykes --- orderer/consensus/etcdraft/consenter_test.go | 162 ++++++++---------- .../consensus/etcdraft/etcdraft_suite_test.go | 3 - .../testdata/consenters_certs/ca1/ca.pem | 12 -- .../testdata/consenters_certs/ca1/client1.pem | 12 -- .../consenters_certs/ca1/client1_pk.pem | 5 - .../testdata/consenters_certs/ca1/client2.pem | 12 -- .../consenters_certs/ca1/client2_pk.pem | 5 - .../testdata/consenters_certs/ca1/client3.pem | 11 -- .../consenters_certs/ca1/client3_pk.pem | 5 - .../testdata/consenters_certs/ca2/ca.pem | 12 -- .../testdata/consenters_certs/ca2/client.pem | 12 -- .../consenters_certs/ca2/client_pk.pem | 5 - orderer/consensus/etcdraft/util_test.go | 103 ++++++----- 13 files changed, 124 insertions(+), 235 deletions(-) delete mode 100644 orderer/consensus/etcdraft/testdata/consenters_certs/ca1/ca.pem delete mode 100644 orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client1.pem delete mode 100644 orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client1_pk.pem delete mode 100644 orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client2.pem delete mode 100644 orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client2_pk.pem delete mode 100644 orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client3.pem delete mode 100644 orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client3_pk.pem delete mode 100644 orderer/consensus/etcdraft/testdata/consenters_certs/ca2/ca.pem delete mode 100644 orderer/consensus/etcdraft/testdata/consenters_certs/ca2/client.pem delete mode 100644 orderer/consensus/etcdraft/testdata/consenters_certs/ca2/client_pk.pem diff --git a/orderer/consensus/etcdraft/consenter_test.go b/orderer/consensus/etcdraft/consenter_test.go index 639bc236214..109c3e05c65 100644 --- a/orderer/consensus/etcdraft/consenter_test.go +++ b/orderer/consensus/etcdraft/consenter_test.go @@ -39,54 +39,35 @@ import ( consensusmocks "github.com/hyperledger/fabric/orderer/consensus/mocks" "github.com/hyperledger/fabric/protoutil" . "github.com/onsi/ginkgo" - "github.com/onsi/ginkgo/extensions/table" + . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" + gtypes "github.com/onsi/gomega/types" "github.com/pkg/errors" "github.com/stretchr/testify/mock" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) -// These fixtures contain certificates for testing consenters. -// In both folders certificates generated using tlsgen pkg, each certificate is singed by ca.pem inside corresponding folder. -// Each cert has 10years expiration time (tlsgenCa.NewServerCertKeyPair("localhost")). - -// NOTE ONLY FOR GO 1.15+: prior to go1.15 tlsgen produced CA root cert without SubjectKeyId, which is not allowed by MSP validator. -// In this test left tags @ONLY-GO1.15+ in places where fixtures can be replaced with tlsgen runtime generated certs once fabric moved to 1.15 - -const ( - consentersTestDataDir = "testdata/consenters_certs/" - ca1Dir = consentersTestDataDir + "ca1" - ca2Dir = consentersTestDataDir + "ca2" -) - var certAsPEM []byte //go:generate counterfeiter -o mocks/orderer_capabilities.go --fake-name OrdererCapabilities . ordererCapabilities - type ordererCapabilities interface { channelconfig.OrdererCapabilities } //go:generate counterfeiter -o mocks/orderer_config.go --fake-name OrdererConfig . ordererConfig - type ordererConfig interface { channelconfig.Orderer } var _ = Describe("Consenter", func() { var ( - chainManager *mocks.ChainManager - support *consensusmocks.FakeConsenterSupport - dataDir string - snapDir string - walDir string - mspDir string - genesisBlockApp *common.Block - confAppRaft *genesisconfig.Profile - tlsCA tlsgen.CA - tlsCa1Cert []byte - tlsCa2Cert []byte + chainManager *mocks.ChainManager + support *consensusmocks.FakeConsenterSupport + dataDir string + snapDir string + walDir string + tlsCA tlsgen.CA ) BeforeEach(func() { @@ -124,13 +105,12 @@ var _ = Describe("Consenter", func() { } support.BlockReturns(lastBlock) - genesisBlockApp = nil - confAppRaft = nil }) AfterEach(func() { - os.RemoveAll(dataDir) - os.RemoveAll(mspDir) + if dataDir != "" { + os.RemoveAll(dataDir) + } }) When("the consenter is extracting the channel", func() { @@ -139,11 +119,13 @@ var _ = Describe("Consenter", func() { ch := consenter.TargetChannel(&orderer.ConsensusRequest{Channel: "mychannel"}) Expect(ch).To(BeIdenticalTo("mychannel")) }) + It("extracts successfully from submit requests", func() { consenter := newConsenter(chainManager, tlsCA.CertBytes(), certAsPEM) ch := consenter.TargetChannel(&orderer.SubmitRequest{Channel: "mychannel"}) Expect(ch).To(BeIdenticalTo("mychannel")) }) + It("returns an empty string for the rest of the messages", func() { consenter := newConsenter(chainManager, tlsCA.CertBytes(), certAsPEM) ch := consenter.TargetChannel(&common.Block{}) @@ -151,45 +133,48 @@ var _ = Describe("Consenter", func() { }) }) + DescribeTable("identifies a bad block", + func(block *common.Block, errMatcher gtypes.GomegaMatcher) { + consenter := newConsenter(chainManager, tlsCA.CertBytes(), certAsPEM) + isMem, err := consenter.IsChannelMember(block) + Expect(isMem).To(BeFalse()) + Expect(err).To(errMatcher) + }, + Entry("nil block", nil, MatchError("nil block")), + Entry("data is nil", &common.Block{}, MatchError("block data is nil")), + Entry("data is empty", protoutil.NewBlock(10, []byte{1, 2, 3, 4}), MatchError("envelope index out of bounds")), + Entry("bad data", + func() *common.Block { + block := protoutil.NewBlock(10, []byte{1, 2, 3, 4}) + block.Data.Data = [][]byte{{1, 2, 3, 4}, {5, 6, 7, 8}} + return block + }(), + MatchError(HavePrefix("block data does not carry an envelope at index 0: error unmarshaling Envelope: proto:"))), + ) + When("the consenter is asked about join-block membership", func() { - table.DescribeTable("identifies a bad block", - func(block *common.Block, errExpected string) { - consenter := newConsenter(chainManager, tlsCA.CertBytes(), certAsPEM) - isMem, err := consenter.IsChannelMember(block) - Expect(isMem).To(BeFalse()) - Expect(err).To(MatchError(errExpected)) - }, - table.Entry("nil block", nil, "nil block"), - table.Entry("data is nil", &common.Block{}, "block data is nil"), - table.Entry("data is empty", protoutil.NewBlock(10, []byte{1, 2, 3, 4}), "envelope index out of bounds"), - table.Entry("bad data", - func() *common.Block { - block := protoutil.NewBlock(10, []byte{1, 2, 3, 4}) - block.Data.Data = [][]byte{{1, 2, 3, 4}, {5, 6, 7, 8}} - return block - }(), - "block data does not carry an envelope at index 0: error unmarshaling Envelope: proto: common.Envelope: illegal tag 0 (wire type 1)"), + var ( + mspDir string + memberKeyPair *tlsgen.CertKeyPair + genesisBlockApp *common.Block + confAppRaft *genesisconfig.Profile ) BeforeEach(func() { + var err error + mspDir, err = ioutil.TempDir(dataDir, "msp") + Expect(err).NotTo(HaveOccurred()) + confAppRaft = genesisconfig.Load(genesisconfig.SampleDevModeEtcdRaftProfile, configtest.GetDevConfigDir()) confAppRaft.Consortiums = nil confAppRaft.Consortium = "" - //@ONLY-GO1.15+ - //it won't be needed, global var tlsCA will be used instead - var err error - tlsCa1Cert, err = ioutil.ReadFile(filepath.Join(ca1Dir, "ca.pem")) - Expect(err).NotTo(HaveOccurred()) - tlsCa2Cert, err = ioutil.ReadFile(filepath.Join(ca2Dir, "ca.pem")) - Expect(err).NotTo(HaveOccurred()) - - // IsChannelMember verifies config meta along with it's tls certs of consenters. - // So when we add new conseter with tls certs, they must be signed by any msp from orderer config. - // Consenters in this test will have certificates from fixtures generated by tlsgen pkg. To pass validation, root ca cert should be part of a MSP in orderer config. + // IsChannelMember verifies config meta along with it's tls certs + // ofconsenters. So when we add new conseter with tls certs, they must be + // signed by any msp from orderer config. Consenters in this test will + // have certificates from fixtures generated by tlsgen pkg. To pass + // validation, root ca cert should be part of a MSP in orderer config. // Adding tls ca root cert to an existing ordering org's MSP definition. - mspDir, err = ioutil.TempDir("", "msp-") - Expect(err).NotTo(HaveOccurred()) Expect(confAppRaft.Orderer).NotTo(BeNil()) Expect(confAppRaft.Orderer.Organizations).ToNot(HaveLen(0)) Expect(confAppRaft.Orderer.EtcdRaft.Consenters).ToNot(HaveLen(0)) @@ -197,9 +182,15 @@ var _ = Describe("Consenter", func() { // one consenter is enough for testing confAppRaft.Orderer.EtcdRaft.Consenters = confAppRaft.Orderer.EtcdRaft.Consenters[:1] - //@ONLY-GO1.15+ - //Here we would generate client pair using tlsCA and set it to the consenter - consenterCertPath := filepath.Join(ca1Dir, "client1.pem") + // Generate client pair using tlsCA and set it to the consenter + memberKeyPair, err = tlsCA.NewServerCertKeyPair("127.0.0.1", "::1", "localhost") + Expect(err).NotTo(HaveOccurred()) + consenterDir, err := ioutil.TempDir(dataDir, "consenter") + Expect(err).NotTo(HaveOccurred()) + consenterCertPath := filepath.Join(consenterDir, "client.pem") + err = ioutil.WriteFile(consenterCertPath, memberKeyPair.Cert, 0o644) + Expect(err).NotTo(HaveOccurred()) + confAppRaft.Orderer.EtcdRaft.Consenters[0].ClientTlsCert = []byte(consenterCertPath) confAppRaft.Orderer.EtcdRaft.Consenters[0].ServerTlsCert = []byte(consenterCertPath) @@ -209,10 +200,8 @@ var _ = Describe("Consenter", func() { confAppRaft.Orderer.Organizations[0].MSPDir = mspDir confAppRaft.Orderer.Organizations[0].ID = fmt.Sprintf("SampleMSP-%d", time.Now().UnixNano()) - // writing tls root cert to msp folder - // ONLY-GO1.15+ Here we would write tlsCA.CertBytes() instead - err = ioutil.WriteFile(filepath.Join(mspDir, "tlscacerts", "cert.pem"), tlsCa1Cert, 0o644) - + // Write the TLS root cert to the msp folder + err = ioutil.WriteFile(filepath.Join(mspDir, "tlscacerts", "cert.pem"), tlsCA.CertBytes(), 0o644) Expect(err).NotTo(HaveOccurred()) bootstrapper, err := encoder.NewBootstrapper(confAppRaft) @@ -222,43 +211,41 @@ var _ = Describe("Consenter", func() { }) It("identifies a member block", func() { - // ONLY-GO1.15+ - // Generate cert using tlsCA.NewClientCertKeyPair() - - consenterCert, err := ioutil.ReadFile(filepath.Join(ca1Dir, "client1.pem")) - Expect(err).NotTo(HaveOccurred()) - - consenter := newConsenter(chainManager, tlsCa1Cert, consenterCert) - + consenter := newConsenter(chainManager, tlsCA.CertBytes(), memberKeyPair.Cert) isMem, err := consenter.IsChannelMember(genesisBlockApp) Expect(isMem).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) }) It("identifies a non-member block", func() { - // ONLY-GO1.15+ - // Generate cert using tlsCA.NewClientCertKeyPair() - - nonMemberConsenterCert, err := ioutil.ReadFile(filepath.Join(ca1Dir, "client2.pem")) + foreignCA, err := tlsgen.NewCA() + Expect(err).NotTo(HaveOccurred()) + nonMemberKeyPair, err := foreignCA.NewServerCertKeyPair("127.0.0.1", "::1", "localhost") Expect(err).NotTo(HaveOccurred()) - consenter := newConsenter(chainManager, tlsCa1Cert, nonMemberConsenterCert) + consenter := newConsenter(chainManager, tlsCA.CertBytes(), nonMemberKeyPair.Cert) isMem, err := consenter.IsChannelMember(genesisBlockApp) Expect(isMem).To(BeFalse()) Expect(err).NotTo(HaveOccurred()) }) It("raft config has consenter with certificate that is not signed by any msp", func() { - // ONLY-GO1.15+ - // Create new ca using tlsgen.NewCA() and generate certificate. New tls root cert won't be part of MSP. + // This TLS root cert will not be part of the MSP. + foreignCA, err := tlsgen.NewCA() + Expect(err).NotTo(HaveOccurred()) + foreignKeyPair, err := foreignCA.NewServerCertKeyPair("127.0.0.1", "::1", "localhost") + Expect(err).NotTo(HaveOccurred()) - foreignConsenterCertPath := filepath.Join(ca2Dir, "client.pem") - foreignConsenterCert, err := ioutil.ReadFile(foreignConsenterCertPath) + consenterDir, err := ioutil.TempDir(dataDir, "foreign-consenter") Expect(err).NotTo(HaveOccurred()) + foreignConsenterCertPath := filepath.Join(consenterDir, "client.pem") + err = ioutil.WriteFile(foreignConsenterCertPath, foreignKeyPair.Cert, 0o644) + Expect(err).NotTo(HaveOccurred()) + confAppRaft.Orderer.EtcdRaft.Consenters[0].ClientTlsCert = []byte(foreignConsenterCertPath) confAppRaft.Orderer.EtcdRaft.Consenters[0].ServerTlsCert = []byte(foreignConsenterCertPath) - consenter := newConsenter(chainManager, tlsCa2Cert, foreignConsenterCert) + consenter := newConsenter(chainManager, foreignCA.CertBytes(), foreignKeyPair.Cert) bootstrapper, err := encoder.NewBootstrapper(confAppRaft) Expect(err).NotTo(HaveOccurred()) @@ -286,6 +273,7 @@ var _ = Describe("Consenter", func() { } } }) + It("calls the chain manager and returns the reference when it is found", func() { consenter := newConsenter(chainManager, tlsCA.CertBytes(), certAsPEM) Expect(consenter).NotTo(BeNil()) @@ -294,6 +282,7 @@ var _ = Describe("Consenter", func() { Expect(chain).NotTo(BeNil()) Expect(chain).To(BeIdenticalTo(chainInstance)) }) + It("calls the chain manager and returns nil when it's not found", func() { consenter := newConsenter(chainManager, tlsCA.CertBytes(), certAsPEM) Expect(consenter).NotTo(BeNil()) @@ -301,6 +290,7 @@ var _ = Describe("Consenter", func() { chain := consenter.ReceiverByChain("notmychannel") Expect(chain).To(BeNil()) }) + It("calls the chain manager and returns nil when it's not a raft chain", func() { consenter := newConsenter(chainManager, tlsCA.CertBytes(), certAsPEM) Expect(consenter).NotTo(BeNil()) diff --git a/orderer/consensus/etcdraft/etcdraft_suite_test.go b/orderer/consensus/etcdraft/etcdraft_suite_test.go index 2008a5f0921..c2d21ceaf40 100644 --- a/orderer/consensus/etcdraft/etcdraft_suite_test.go +++ b/orderer/consensus/etcdraft/etcdraft_suite_test.go @@ -14,10 +14,7 @@ import ( . "github.com/onsi/gomega" ) -var testingInstance *testing.T - func TestEtcdraft(t *testing.T) { - testingInstance = t RegisterFailHandler(Fail) RunSpecs(t, "Etcdraft Suite") } diff --git a/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/ca.pem b/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/ca.pem deleted file mode 100644 index b19793d7cca..00000000000 --- a/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/ca.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBxTCCAWqgAwIBAgIRAJfE/nlTLmP8FXwo1iQppRIwCgYIKoZIzj0EAwIwMjEw -MC4GA1UEBRMnMjAxNzM2Mjc4ODkyMTg1NDUzMzQxMjIyNzU2MzkxNDgzNzEyNzg2 -MB4XDTIwMTAwMTEzNDcwOFoXDTMwMDkzMDEzNDcwOFowMjEwMC4GA1UEBRMnMjAx -NzM2Mjc4ODkyMTg1NDUzMzQxMjIyNzU2MzkxNDgzNzEyNzg2MFkwEwYHKoZIzj0C -AQYIKoZIzj0DAQcDQgAEVJiaycMxW/VTrHMv0cme6CLSItWCyX0dra0qqV6qkcfb -6/ZTNuGHU04KUuPEFObHpFhJhzwP4MPBFkWLARj05aNhMF8wDgYDVR0PAQH/BAQD -AgGmMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTAD -AQH/MB0GA1UdDgQWBBRCqpOXs1eIfE4DF7Wuq2f6dfyx6DAKBggqhkjOPQQDAgNJ -ADBGAiEAl4CZfgRBElX2gTHOaRUQEcNROyqjmLfgnzGZwgwT2jkCIQDkXc0zh2tF -Oe8uRH7h/89avK8HPIX9baWqJYGMoqJwBg== ------END CERTIFICATE----- diff --git a/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client1.pem b/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client1.pem deleted file mode 100644 index b3edafca241..00000000000 --- a/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client1.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIByDCCAW+gAwIBAgIQFNyzxFBsjeZGDJIuMF39cTAKBggqhkjOPQQDAjAyMTAw -LgYDVQQFEycyMDE3MzYyNzg4OTIxODU0NTMzNDEyMjI3NTYzOTE0ODM3MTI3ODYw -HhcNMjAxMDAxMTM0NzA4WhcNMzAwOTMwMTM0NzA4WjAxMS8wLQYDVQQFEyYyNzcz -MDUxMTMyOTUwNDkyMDg1Njg2NzY4MjMyMjEwNTYzMDA2NTBZMBMGByqGSM49AgEG -CCqGSM49AwEHA0IABGLpDGpW8C580MeIMz2zZJKif1RsAEWbJP5AbaHA7h+Xd5PK -ud7ZmX17F+UY9wWdlRhzaHKUGHuvx7Zs/3YUjwajaDBmMA4GA1UdDwEB/wQEAwIF -oDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwHwYDVR0jBBgwFoAUQqqT -l7NXiHxOAxe1rqtn+nX8segwFAYDVR0RBA0wC4IJbG9jYWxob3N0MAoGCCqGSM49 -BAMCA0cAMEQCIAyj/Y6bEiBdY/xCSP1a0T4gzpsp+4nf8lP8AETtCxvmAiA0VvN0 -pIugeN4cTFLSH5ZUFna+B0uatSV/VD5iR54gBQ== ------END CERTIFICATE----- diff --git a/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client1_pk.pem b/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client1_pk.pem deleted file mode 100644 index 003c124af3e..00000000000 --- a/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client1_pk.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgRyUBXdmZyYTkS6uY -+iDZRHtCxd81vx8cXMx2NI0YGN6hRANCAARi6QxqVvAufNDHiDM9s2SSon9UbABF -myT+QG2hwO4fl3eTyrne2Zl9exflGPcFnZUYc2hylBh7r8e2bP92FI8G ------END EC PRIVATE KEY----- diff --git a/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client2.pem b/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client2.pem deleted file mode 100644 index 13a63d6005a..00000000000 --- a/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client2.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIByjCCAXCgAwIBAgIQWoXZ/0K4ckeGSjVj6hpYqDAKBggqhkjOPQQDAjAyMTAw -LgYDVQQFEycyMDE3MzYyNzg4OTIxODU0NTMzNDEyMjI3NTYzOTE0ODM3MTI3ODYw -HhcNMjAxMDAxMTM0NzA4WhcNMzAwOTMwMTM0NzA4WjAyMTAwLgYDVQQFEycxMjAz -MjU1MTY2MDk1NDE5ODY4NTQwMTc2MjI0NzM3NzIyNTk0OTYwWTATBgcqhkjOPQIB -BggqhkjOPQMBBwNCAARIVQhSUGQ3zpdgAYcvSLi5JqaofLqQYYRxVTFJgAk49xES -F4xu90KfpFXw6Py/Ez1Vohbne61YUI+VNWgE1234o2gwZjAOBgNVHQ8BAf8EBAMC -BaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB8GA1UdIwQYMBaAFEKq -k5ezV4h8TgMXta6rZ/p1/LHoMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDAKBggqhkjO -PQQDAgNIADBFAiAkapytxIXMWfMaeQJ6hqzzuuirD/r50GYD30DFJucVXwIhAI2u -yDYgGr0XFvEWO4VFrw2PrG5GPeafVekQOzhzfux+ ------END CERTIFICATE----- diff --git a/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client2_pk.pem b/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client2_pk.pem deleted file mode 100644 index f0113c3c446..00000000000 --- a/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client2_pk.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgIetvFmxk3Qvc+EAL -odmO0/TdGda6DZXbE3nLULLN592hRANCAARIVQhSUGQ3zpdgAYcvSLi5JqaofLqQ -YYRxVTFJgAk49xESF4xu90KfpFXw6Py/Ez1Vohbne61YUI+VNWgE1234 ------END EC PRIVATE KEY----- diff --git a/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client3.pem b/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client3.pem deleted file mode 100644 index e8eaaf29e44..00000000000 --- a/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client3.pem +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBqjCCAU+gAwIBAgIQK3VSM8NZjTTUe16wYCcpczAKBggqhkjOPQQDAjAyMTAw -LgYDVQQFEycyMDE3MzYyNzg4OTIxODU0NTMzNDEyMjI3NTYzOTE0ODM3MTI3ODYw -HhcNMjAxMDAxMTM0NzA4WhcNMjAxMDAxMTQ0NzA4WjAxMS8wLQYDVQQFEyY1Nzc2 -NTk2OTgwOTg4MTU4MzE3MzE4MTQ1NDkyODQ3MDAyNjYxMTBZMBMGByqGSM49AgEG -CCqGSM49AwEHA0IABL3+dJ2+Vgp/RFpVaL/pIHfHEidNwCA4ILLmzeRty1nF8cki -xb37KZ2qsnzhmRMWbzZsslPJIGN9waJiLHyhtRejSDBGMA4GA1UdDwEB/wQEAwIF -oDATBgNVHSUEDDAKBggrBgEFBQcDAjAfBgNVHSMEGDAWgBRCqpOXs1eIfE4DF7Wu -q2f6dfyx6DAKBggqhkjOPQQDAgNJADBGAiEAmKOQBDL7OPskAzvdDEn66SG39fZu -0/882H7hEahhq4UCIQCkpRYzXgWDiWWmlsgYR1xrq5NOF/qykeBFGk6j9VCh9w== ------END CERTIFICATE----- diff --git a/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client3_pk.pem b/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client3_pk.pem deleted file mode 100644 index c2aa9b0f078..00000000000 --- a/orderer/consensus/etcdraft/testdata/consenters_certs/ca1/client3_pk.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgH7cx3pIIDtIjtqDp -xf463iPh52aQ24u+6qz1rDInw32hRANCAAS9/nSdvlYKf0RaVWi/6SB3xxInTcAg -OCCy5s3kbctZxfHJIsW9+ymdqrJ84ZkTFm82bLJTySBjfcGiYix8obUX ------END EC PRIVATE KEY----- diff --git a/orderer/consensus/etcdraft/testdata/consenters_certs/ca2/ca.pem b/orderer/consensus/etcdraft/testdata/consenters_certs/ca2/ca.pem deleted file mode 100644 index 301fda7006b..00000000000 --- a/orderer/consensus/etcdraft/testdata/consenters_certs/ca2/ca.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBwzCCAWmgAwIBAgIQZnkg9cSdw8r/8jQYT8vzYTAKBggqhkjOPQQDAjAyMTAw -LgYDVQQFEycxMzYyMTAxOTE5OTg4MDEwNTM5MTIyNjkyNDAzNTg0MzAwNDUwMjUw -HhcNMjAxMDAxMTM0NzA4WhcNMzAwOTMwMTM0NzA4WjAyMTAwLgYDVQQFEycxMzYy -MTAxOTE5OTg4MDEwNTM5MTIyNjkyNDAzNTg0MzAwNDUwMjUwWTATBgcqhkjOPQIB -BggqhkjOPQMBBwNCAAQjZhuqnSD+r/buwX4tv8zuzbyGZgi38Gxx0euvd2RAEzkk -cc+DFxgXqaFbi1Qh+iie9zGfNkrlRiiwQRRUqQq0o2EwXzAOBgNVHQ8BAf8EBAMC -AaYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMB -Af8wHQYDVR0OBBYEFIZNvoJDGEwj2D2X27Hu8le34nrWMAoGCCqGSM49BAMCA0gA -MEUCID6eZk263mIZtpck2jSnUutrBj//l168R+eNQfS4HGOBAiEAz3pE09O0CD/V -INyTIafVHr3kX3G+qHvDBO7KIQHLFCs= ------END CERTIFICATE----- diff --git a/orderer/consensus/etcdraft/testdata/consenters_certs/ca2/client.pem b/orderer/consensus/etcdraft/testdata/consenters_certs/ca2/client.pem deleted file mode 100644 index a853d4e2795..00000000000 --- a/orderer/consensus/etcdraft/testdata/consenters_certs/ca2/client.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIByzCCAXGgAwIBAgIRAJC9JBJEUtMrQU3ykMufdxUwCgYIKoZIzj0EAwIwMjEw -MC4GA1UEBRMnMTM2MjEwMTkxOTk4ODAxMDUzOTEyMjY5MjQwMzU4NDMwMDQ1MDI1 -MB4XDTIwMTAwMTEzNDcwOFoXDTMwMDkzMDEzNDcwOFowMjEwMC4GA1UEBRMnMTky -MzkwOTA3MTEzMjg4NzM0NjM5MTM3MjQ4MDkxMDY4OTIxNjIxMFkwEwYHKoZIzj0C -AQYIKoZIzj0DAQcDQgAEugVus3vzHpIPhyCyWOOTCG+9DdO4SwaGg+UF6l4ck/Vr -RPkZX2+EGiSCEe9Pqd6A47HaNS02nA+Cyeopa6th3KNoMGYwDgYDVR0PAQH/BAQD -AgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAfBgNVHSMEGDAWgBSG -Tb6CQxhMI9g9l9ux7vJXt+J61jAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYIKoZI -zj0EAwIDSAAwRQIhAJSpx4u2NklmITJjaIHLBpDtKdImYM2eawBIsZsENmozAiBY -D99gaeVF+b91sKy1nVF2dzoXybvACfiRcSu8kMLFpQ== ------END CERTIFICATE----- diff --git a/orderer/consensus/etcdraft/testdata/consenters_certs/ca2/client_pk.pem b/orderer/consensus/etcdraft/testdata/consenters_certs/ca2/client_pk.pem deleted file mode 100644 index 1a7b9f6699c..00000000000 --- a/orderer/consensus/etcdraft/testdata/consenters_certs/ca2/client_pk.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8aytZd7Q3vNvZA28 -FSSn+14zOuib6LSX4Nm2NdqDwWmhRANCAAS1cFWuFQkPnc6hb71hrULuP3FcnNFS -ny8Cuu4SuVubTuhM6tQeZzr1RD6LkQPq7DH+kESKti9VSqZmrX2yvTK9 ------END EC PRIVATE KEY----- diff --git a/orderer/consensus/etcdraft/util_test.go b/orderer/consensus/etcdraft/util_test.go index 7e7ca9f40e4..b7ca5f26c45 100644 --- a/orderer/consensus/etcdraft/util_test.go +++ b/orderer/consensus/etcdraft/util_test.go @@ -7,12 +7,15 @@ SPDX-License-Identifier: Apache-2.0 package etcdraft import ( + "crypto/rand" "crypto/x509" "encoding/base64" + "encoding/pem" "fmt" "io/ioutil" "path/filepath" "testing" + "time" "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric-protos-go/common" @@ -26,12 +29,6 @@ import ( "github.com/stretchr/testify/require" ) -const ( - consentersTestDataDir = "testdata/consenters_certs/" - ca1Dir = consentersTestDataDir + "ca1" - ca2Dir = consentersTestDataDir + "ca2" -) - func TestIsConsenterOfChannel(t *testing.T) { certInsideConfigBlock, err := base64.StdEncoding.DecodeString("LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNmekNDQWlhZ0F3SUJBZ0l" + "SQUo4bjFLYTVzS1ZaTXRMTHJ1dldERDB3Q2dZSUtvWkl6ajBFQXdJd2JERUwKTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnVENrTmhiR" + @@ -128,49 +125,31 @@ func TestIsConsenterOfChannel(t *testing.T) { func TestVerifyConfigMetadata(t *testing.T) { tlsCA, err := tlsgen.NewCA() - if err != nil { - panic(err) - } + require.NoError(t, err, "failed to create CA") caRootCert, err := parseCertificateFromBytes(tlsCA.CertBytes()) - if err != nil { - panic(err) - } + require.NoError(t, err, "failed to parse CA certificate") serverPair, err := tlsCA.NewServerCertKeyPair("localhost") - if err != nil { - panic(err) - } + require.NoError(t, err, "failed to create server key pair") clientPair, err := tlsCA.NewClientCertKeyPair() - if err != nil { - panic(err) - } + require.NoError(t, err, "failed to create client key pair") unknownTlsCA, err := tlsgen.NewCA() - if err != nil { - panic(err) - } + require.NoError(t, err, "failed to create unknown CA") unknownServerPair, err := unknownTlsCA.NewServerCertKeyPair("unknownhost") - if err != nil { - panic(err) - } + require.NoError(t, err, "failed to create unknown server key pair") unknownServerCert, err := parseCertificateFromBytes(unknownServerPair.Cert) - if err != nil { - panic(err) - } + require.NoError(t, err, "failed to parse unknown server certificate") unknownClientPair, err := unknownTlsCA.NewClientCertKeyPair() - if err != nil { - panic(err) - } + require.NoError(t, err, "failed to create unknown client key pair") unknownClientCert, err := parseCertificateFromBytes(unknownClientPair.Cert) - if err != nil { - panic(err) - } + require.NoError(t, err, "failed to parse unknown client certificate") validOptions := &etcdraftproto.Options{ TickInterval: "500ms", @@ -391,30 +370,44 @@ func TestVerifyConfigMetadata(t *testing.T) { }) } - // test use case when consenter has expired certificates - tlsCaCertBytes, err := ioutil.ReadFile(filepath.Join(ca1Dir, "ca.pem")) - require.Nil(t, err) - tlsCaCert, err := parseCertificateFromBytes(tlsCaCertBytes) - require.Nil(t, err) + t.Run("ExpiredCertificate", func(t *testing.T) { + clientPair, err := tlsCA.NewClientCertKeyPair() + require.NoError(t, err, "failed to create client key pair") - tlsClientCert, err := ioutil.ReadFile(filepath.Join(ca1Dir, "client3.pem")) - require.Nil(t, err) + clientCert := clientPair.TLSCert + clientCert.NotAfter = time.Now().Add(-24 * time.Hour) + clientCertBytes, err := x509.CreateCertificate(rand.Reader, clientCert, caRootCert, clientPair.Signer.Public(), tlsCA.Signer()) + require.NoError(t, err, "failed to create expired certificate") - expiredCertVerifyOpts := goodVerifyingOpts - expiredCertVerifyOpts.Roots.AddCert(tlsCaCert) - consenterWithExpiredCerts := &etcdraftproto.Consenter{ - Host: "host1", - Port: 10001, - ClientTlsCert: tlsClientCert, - ServerTlsCert: tlsClientCert, - } + clientCert, err = x509.ParseCertificate(clientCertBytes) + require.NoError(t, err, "failed to parse expired certificate") - metadataWithExpiredConsenter := &etcdraftproto.ConfigMetadata{ - Options: validOptions, - Consenters: []*etcdraftproto.Consenter{ - consenterWithExpiredCerts, - }, - } + _, err = clientCert.Verify(goodVerifyingOpts) + require.Error(t, err, "expected certificate verification to fail") + cie, ok := err.(x509.CertificateInvalidError) + require.True(t, ok, "expected an x509.CertificateInvalidError but got %T", err) + require.Equal(t, x509.Expired, cie.Reason) + + consenterWithExpiredCerts := &etcdraftproto.Consenter{ + Host: "host1", + Port: 10001, + ClientTlsCert: pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: clientCertBytes}), + ServerTlsCert: serverPair.Cert, + } + + metadataWithExpiredConsenter := &etcdraftproto.ConfigMetadata{ + Options: &etcdraftproto.Options{ + TickInterval: "500ms", + ElectionTick: 10, + HeartbeatTick: 1, + MaxInflightBlocks: 5, + SnapshotIntervalSize: 20 * 1024 * 1024, // 20 MB + }, + Consenters: []*etcdraftproto.Consenter{ + consenterWithExpiredCerts, + }, + } - require.Nil(t, VerifyConfigMetadata(metadataWithExpiredConsenter, expiredCertVerifyOpts)) + require.Nil(t, VerifyConfigMetadata(metadataWithExpiredConsenter, goodVerifyingOpts)) + }) }