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