github.com/defanghe/fabric@v2.1.1+incompatible/gossip/comm/crypto_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package comm
     8  
     9  import (
    10  	"context"
    11  	"crypto/ecdsa"
    12  	"crypto/elliptic"
    13  	"crypto/rand"
    14  	"crypto/tls"
    15  	"crypto/x509"
    16  	"encoding/pem"
    17  	"fmt"
    18  	"math/big"
    19  	"net"
    20  	"sync"
    21  	"testing"
    22  	"time"
    23  
    24  	proto "github.com/hyperledger/fabric-protos-go/gossip"
    25  	"github.com/hyperledger/fabric/gossip/util"
    26  	"github.com/stretchr/testify/assert"
    27  	"google.golang.org/grpc"
    28  	"google.golang.org/grpc/credentials"
    29  )
    30  
    31  type gossipTestServer struct {
    32  	lock           sync.Mutex
    33  	remoteCertHash []byte
    34  	selfCertHash   []byte
    35  	ll             net.Listener
    36  	s              *grpc.Server
    37  }
    38  
    39  func init() {
    40  	util.SetupTestLogging()
    41  }
    42  
    43  func createTestServer(t *testing.T, cert *tls.Certificate) (srv *gossipTestServer, ll net.Listener) {
    44  	tlsConf := &tls.Config{
    45  		Certificates:       []tls.Certificate{*cert},
    46  		ClientAuth:         tls.RequestClientCert,
    47  		InsecureSkipVerify: true,
    48  	}
    49  	s := grpc.NewServer(grpc.Creds(credentials.NewTLS(tlsConf)))
    50  	ll, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:0"))
    51  	assert.NoError(t, err, "%v", err)
    52  
    53  	srv = &gossipTestServer{s: s, ll: ll, selfCertHash: certHashFromRawCert(cert.Certificate[0])}
    54  	proto.RegisterGossipServer(s, srv)
    55  	go s.Serve(ll)
    56  	return srv, ll
    57  }
    58  
    59  func (s *gossipTestServer) stop() {
    60  	s.s.Stop()
    61  	s.ll.Close()
    62  }
    63  
    64  func (s *gossipTestServer) GossipStream(stream proto.Gossip_GossipStreamServer) error {
    65  	s.lock.Lock()
    66  	defer s.lock.Unlock()
    67  	s.remoteCertHash = extractCertificateHashFromContext(stream.Context())
    68  	return nil
    69  }
    70  
    71  func (s *gossipTestServer) getClientCertHash() []byte {
    72  	s.lock.Lock()
    73  	defer s.lock.Unlock()
    74  	return s.remoteCertHash
    75  }
    76  
    77  func (s *gossipTestServer) Ping(context.Context, *proto.Empty) (*proto.Empty, error) {
    78  	return &proto.Empty{}, nil
    79  }
    80  
    81  func TestCertificateExtraction(t *testing.T) {
    82  	cert := GenerateCertificatesOrPanic()
    83  	srv, ll := createTestServer(t, &cert)
    84  	defer srv.stop()
    85  
    86  	clientCert := GenerateCertificatesOrPanic()
    87  	clientCertHash := certHashFromRawCert(clientCert.Certificate[0])
    88  	ta := credentials.NewTLS(&tls.Config{
    89  		Certificates:       []tls.Certificate{clientCert},
    90  		InsecureSkipVerify: true,
    91  	})
    92  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    93  	defer cancel()
    94  	conn, err := grpc.DialContext(ctx, ll.Addr().String(), grpc.WithTransportCredentials(ta), grpc.WithBlock())
    95  	assert.NoError(t, err, "%v", err)
    96  
    97  	cl := proto.NewGossipClient(conn)
    98  	stream, err := cl.GossipStream(context.Background())
    99  	assert.NoError(t, err, "%v", err)
   100  	if err != nil {
   101  		return
   102  	}
   103  
   104  	time.Sleep(time.Second)
   105  	clientSideCertHash := extractCertificateHashFromContext(stream.Context())
   106  	serverSideCertHash := srv.getClientCertHash()
   107  
   108  	assert.NotNil(t, clientSideCertHash)
   109  	assert.NotNil(t, serverSideCertHash)
   110  
   111  	assert.Equal(t, 32, len(clientSideCertHash), "client side cert hash is %v", clientSideCertHash)
   112  	assert.Equal(t, 32, len(serverSideCertHash), "server side cert hash is %v", serverSideCertHash)
   113  
   114  	assert.Equal(t, clientSideCertHash, srv.selfCertHash, "Server self hash isn't equal to client side hash")
   115  	assert.Equal(t, clientCertHash, srv.remoteCertHash, "Server side and client hash aren't equal")
   116  }
   117  
   118  // GenerateCertificatesOrPanic generates a a random pair of public and private keys
   119  // and return TLS certificate.
   120  func GenerateCertificatesOrPanic() tls.Certificate {
   121  	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   122  	if err != nil {
   123  		panic(err)
   124  	}
   125  	sn, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
   126  	if err != nil {
   127  		panic(err)
   128  	}
   129  	template := x509.Certificate{
   130  		KeyUsage:     x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
   131  		SerialNumber: sn,
   132  		ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   133  	}
   134  	rawBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
   135  	if err != nil {
   136  		panic(err)
   137  	}
   138  	privBytes, err := x509.MarshalECPrivateKey(privateKey)
   139  	if err != nil {
   140  		panic(err)
   141  	}
   142  	encodedCert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rawBytes})
   143  	encodedKey := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: privBytes})
   144  	cert, err := tls.X509KeyPair(encodedCert, encodedKey)
   145  	if err != nil {
   146  		panic(err)
   147  	}
   148  	return cert
   149  }