github.com/koko1123/flow-go-1@v0.29.6/utils/grpcutils/grpc.go (about)

     1  package grpcutils
     2  
     3  import (
     4  	"crypto/tls"
     5  	"crypto/x509"
     6  	"encoding/hex"
     7  	"fmt"
     8  
     9  	libp2ptls "github.com/libp2p/go-libp2p-tls"
    10  	lcrypto "github.com/libp2p/go-libp2p/core/crypto"
    11  
    12  	"github.com/koko1123/flow-go-1/network/p2p/keyutils"
    13  	"github.com/onflow/flow-go/crypto"
    14  )
    15  
    16  // DefaultMaxMsgSize use 16MB as the default message size limit.
    17  // grpc library default is 4MB
    18  const DefaultMaxMsgSize = 1024 * 1024 * 16
    19  
    20  // X509Certificate generates a self-signed x509 TLS certificate from the given key. The generated certificate
    21  // includes a libp2p extension that specifies the public key and the signature. The certificate does not include any
    22  // SAN extension.
    23  func X509Certificate(privKey crypto.PrivateKey) (*tls.Certificate, error) {
    24  
    25  	// convert the Flow crypto private key to a Libp2p private crypto key
    26  	libP2PKey, err := keyutils.LibP2PPrivKeyFromFlow(privKey)
    27  	if err != nil {
    28  		return nil, fmt.Errorf("could not convert Flow key to libp2p key: %w", err)
    29  	}
    30  
    31  	// create a libp2p Identity from the libp2p private key
    32  	id, err := libp2ptls.NewIdentity(libP2PKey)
    33  	if err != nil {
    34  		return nil, fmt.Errorf("could not generate identity: %w", err)
    35  	}
    36  
    37  	// extract the TLSConfig from it which will contain the generated x509 certificate
    38  	// (ignore the public key that is returned - it is the public key of the private key used to generate the ID)
    39  	libp2pTlsConfig, _ := id.ConfigForPeer("")
    40  
    41  	// verify that exactly one certificate was generated for the given key
    42  	certCount := len(libp2pTlsConfig.Certificates)
    43  	if certCount != 1 {
    44  		return nil, fmt.Errorf("invalid count for the generated x509 certificate: %d", certCount)
    45  	}
    46  
    47  	return &libp2pTlsConfig.Certificates[0], nil
    48  }
    49  
    50  // DefaultServerTLSConfig returns the default TLS server config with the given cert for a secure GRPC server
    51  func DefaultServerTLSConfig(cert *tls.Certificate) *tls.Config {
    52  	tlsConfig := &tls.Config{
    53  		MinVersion:   tls.VersionTLS13,
    54  		Certificates: []tls.Certificate{*cert},
    55  		ClientAuth:   tls.NoClientCert,
    56  	}
    57  	return tlsConfig
    58  }
    59  
    60  // ServerAuthError is an error returned when the server authentication fails
    61  type ServerAuthError struct {
    62  	message string
    63  }
    64  
    65  // newServerAuthError constructs a new ServerAuthError
    66  func newServerAuthError(msg string, args ...interface{}) *ServerAuthError {
    67  	return &ServerAuthError{message: fmt.Sprintf(msg, args...)}
    68  }
    69  
    70  func (e ServerAuthError) Error() string {
    71  	return e.message
    72  }
    73  
    74  // IsServerAuthError checks if the input error is of a ServerAuthError type
    75  func IsServerAuthError(err error) bool {
    76  	_, ok := err.(*ServerAuthError)
    77  	return ok
    78  }
    79  
    80  // DefaultClientTLSConfig returns the default TLS client config with the given public key for a secure GRPC client
    81  // The TLSConfig verifies that the server certifcate is valid and has the correct signature
    82  func DefaultClientTLSConfig(publicKey crypto.PublicKey) (*tls.Config, error) {
    83  
    84  	// #nosec G402
    85  	config := &tls.Config{
    86  		MinVersion: tls.VersionTLS13,
    87  		// This is not insecure here. We will verify the cert chain ourselves.
    88  		InsecureSkipVerify: true,
    89  		ClientAuth:         tls.RequireAnyClientCert,
    90  	}
    91  
    92  	verifyPeerCertFunc, err := verifyPeerCertificateFunc(publicKey)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  	config.VerifyPeerCertificate = verifyPeerCertFunc
    97  
    98  	return config, nil
    99  }
   100  
   101  func verifyPeerCertificateFunc(expectedPublicKey crypto.PublicKey) (func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error, error) {
   102  
   103  	// convert the Flow.crypto key to LibP2P key for easy comparision using LibP2P TLS utils
   104  	remotePeerLibP2PID, err := keyutils.PeerIDFromFlowPublicKey(expectedPublicKey)
   105  	if err != nil {
   106  		return nil, fmt.Errorf("failed to derive the libp2p Peer ID from the Flow key: %w", err)
   107  	}
   108  
   109  	// We're using InsecureSkipVerify, so the verifiedChains parameter will always be empty.
   110  	// We need to parse the certificates ourselves from the raw certs.
   111  	verifyFunc := func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
   112  
   113  		chain := make([]*x509.Certificate, len(rawCerts))
   114  		for i := 0; i < len(rawCerts); i++ {
   115  			cert, err := x509.ParseCertificate(rawCerts[i])
   116  			if err != nil {
   117  				return newServerAuthError(err.Error())
   118  			}
   119  			chain[i] = cert
   120  		}
   121  
   122  		// libp2ptls.PubKeyFromCertChain verifies the certificate, verifies that the certificate contains the special libp2p
   123  		// extension, extract the remote's public key and finally verifies the signature included in the certificate
   124  		actualLibP2PKey, err := libp2ptls.PubKeyFromCertChain(chain)
   125  		if err != nil {
   126  			return newServerAuthError(err.Error())
   127  		}
   128  
   129  		// verify that the public key received is the one that is expected
   130  		if !remotePeerLibP2PID.MatchesPublicKey(actualLibP2PKey) {
   131  			actualKeyHex, err := libP2PKeyToHexString(actualLibP2PKey)
   132  			if err != nil {
   133  				return err
   134  			}
   135  			return newServerAuthError("invalid public key received: expected %s, got %s", expectedPublicKey.String(), actualKeyHex)
   136  		}
   137  		return nil
   138  	}
   139  
   140  	return verifyFunc, nil
   141  }
   142  
   143  func libP2PKeyToHexString(key lcrypto.PubKey) (string, *ServerAuthError) {
   144  	keyRaw, err := key.Raw()
   145  	if err != nil {
   146  		return "", newServerAuthError(err.Error())
   147  	}
   148  	return hex.EncodeToString(keyRaw), nil
   149  }