github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/utils/grpcutils/grpc.go (about)

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