github.com/xmplusdev/xmcore@v1.8.11-0.20240412132628-5518b55526af/transport/internet/tls/grpc.go (about)

     1  package tls
     2  
     3  import (
     4  	"context"
     5  	gotls "crypto/tls"
     6  	"net"
     7  	"net/url"
     8  	"strconv"
     9  
    10  	utls "github.com/refraction-networking/utls"
    11  	"google.golang.org/grpc/credentials"
    12  )
    13  
    14  // grpcUtlsInfo contains the auth information for a TLS authenticated connection.
    15  // It implements the AuthInfo interface.
    16  type grpcUtlsInfo struct {
    17  	State utls.ConnectionState
    18  	credentials.CommonAuthInfo
    19  	// This API is experimental.
    20  	SPIFFEID *url.URL
    21  }
    22  
    23  // AuthType returns the type of TLSInfo as a string.
    24  func (t grpcUtlsInfo) AuthType() string {
    25  	return "utls"
    26  }
    27  
    28  // GetSecurityValue returns security info requested by channelz.
    29  func (t grpcUtlsInfo) GetSecurityValue() credentials.ChannelzSecurityValue {
    30  	v := &credentials.TLSChannelzSecurityValue{
    31  		StandardName: "0x" + strconv.FormatUint(uint64(t.State.CipherSuite), 16),
    32  	}
    33  	// Currently there's no way to get LocalCertificate info from tls package.
    34  	if len(t.State.PeerCertificates) > 0 {
    35  		v.RemoteCertificate = t.State.PeerCertificates[0].Raw
    36  	}
    37  	return v
    38  }
    39  
    40  // grpcUtls is the credentials required for authenticating a connection using TLS.
    41  type grpcUtls struct {
    42  	config      *gotls.Config
    43  	fingerprint *utls.ClientHelloID
    44  }
    45  
    46  func (c grpcUtls) Info() credentials.ProtocolInfo {
    47  	return credentials.ProtocolInfo{
    48  		SecurityProtocol: "tls",
    49  		SecurityVersion:  "1.2",
    50  		ServerName:       c.config.ServerName,
    51  	}
    52  }
    53  
    54  func (c *grpcUtls) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (_ net.Conn, _ credentials.AuthInfo, err error) {
    55  	// use local cfg to avoid clobbering ServerName if using multiple endpoints
    56  	cfg := c.config.Clone()
    57  	if cfg.ServerName == "" {
    58  		serverName, _, err := net.SplitHostPort(authority)
    59  		if err != nil {
    60  			// If the authority had no host port or if the authority cannot be parsed, use it as-is.
    61  			serverName = authority
    62  		}
    63  		cfg.ServerName = serverName
    64  	}
    65  	conn := UClient(rawConn, cfg, c.fingerprint).(*UConn)
    66  	errChannel := make(chan error, 1)
    67  	go func() {
    68  		errChannel <- conn.HandshakeContext(ctx)
    69  		close(errChannel)
    70  	}()
    71  	select {
    72  	case err := <-errChannel:
    73  		if err != nil {
    74  			conn.Close()
    75  			return nil, nil, err
    76  		}
    77  	case <-ctx.Done():
    78  		conn.Close()
    79  		return nil, nil, ctx.Err()
    80  	}
    81  	tlsInfo := grpcUtlsInfo{
    82  		State: conn.ConnectionState(),
    83  		CommonAuthInfo: credentials.CommonAuthInfo{
    84  			SecurityLevel: credentials.PrivacyAndIntegrity,
    85  		},
    86  	}
    87  	return conn, tlsInfo, nil
    88  }
    89  
    90  // ServerHandshake will always panic. We don't support running uTLS as server.
    91  func (c *grpcUtls) ServerHandshake(net.Conn) (net.Conn, credentials.AuthInfo, error) {
    92  	panic("not available!")
    93  }
    94  
    95  func (c *grpcUtls) Clone() credentials.TransportCredentials {
    96  	return NewGrpcUtls(c.config, c.fingerprint)
    97  }
    98  
    99  func (c *grpcUtls) OverrideServerName(serverNameOverride string) error {
   100  	c.config.ServerName = serverNameOverride
   101  	return nil
   102  }
   103  
   104  // NewGrpcUtls uses c to construct a TransportCredentials based on uTLS.
   105  func NewGrpcUtls(c *gotls.Config, fingerprint *utls.ClientHelloID) credentials.TransportCredentials {
   106  	tc := &grpcUtls{c.Clone(), fingerprint}
   107  	return tc
   108  }