lnd/cert/tls.go
2024-04-25 11:22:43 +02:00

152 lines
4.4 KiB
Go

package cert
import (
"crypto/tls"
"crypto/x509"
"os"
"sync"
)
var (
/*
* tlsCipherSuites is the list of cipher suites we accept for TLS
* connections. These cipher suites fit the following criteria:
* - Don't use outdated algorithms like SHA-1 and 3DES
* - Don't use ECB mode or other insecure symmetric methods
* - Included in the TLS v1.2 suite
* - Are available in the Go 1.7.6 standard library (more are
* available in 1.8.3 and will be added after lnd no longer
* supports 1.7, including suites that support CBC mode)
**/
tlsCipherSuites = []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
}
)
// GetCertBytesFromPath reads the TLS certificate and key files at the given
// certPath and keyPath and returns the file bytes.
func GetCertBytesFromPath(certPath, keyPath string) (certBytes,
keyBytes []byte, err error) {
certBytes, err = os.ReadFile(certPath)
if err != nil {
return nil, nil, err
}
keyBytes, err = os.ReadFile(keyPath)
if err != nil {
return nil, nil, err
}
return certBytes, keyBytes, nil
}
// LoadCert loads a certificate and its corresponding private key from the PEM
// files indicated and returns the certificate in the two formats it is most
// commonly used.
func LoadCert(certPath, keyPath string) (tls.Certificate, *x509.Certificate,
error) {
// The certData returned here is just a wrapper around the PEM blocks
// loaded from the file. The PEM is not yet fully parsed but a basic
// check is performed that the certificate and private key actually
// belong together.
certData, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return tls.Certificate{}, nil, err
}
// Now parse the PEM block of the certificate into its x509 data
// structure so it can be examined in more detail.
x509Cert, err := x509.ParseCertificate(certData.Certificate[0])
if err != nil {
return tls.Certificate{}, nil, err
}
return certData, x509Cert, nil
}
// LoadCertFromBytes loads a certificate and its corresponding private key from
// the PEM bytes indicated and returns the certificate in the two formats it is
// most commonly used.
func LoadCertFromBytes(certBytes, keyBytes []byte) (tls.Certificate,
*x509.Certificate, error) {
// The certData returned here is just a wrapper around the PEM blocks
// loaded from the file. The PEM is not yet fully parsed but a basic
// check is performed that the certificate and private key actually
// belong together.
certData, err := tls.X509KeyPair(certBytes, keyBytes)
if err != nil {
return tls.Certificate{}, nil, err
}
// Now parse the PEM block of the certificate into its x509 data
// structure so it can be examined in more detail.
x509Cert, err := x509.ParseCertificate(certData.Certificate[0])
if err != nil {
return tls.Certificate{}, nil, err
}
return certData, x509Cert, nil
}
// TLSConfFromCert returns the default TLS configuration used for a server,
// using the given certificate as identity.
func TLSConfFromCert(certData tls.Certificate) *tls.Config {
return &tls.Config{
Certificates: []tls.Certificate{certData},
CipherSuites: tlsCipherSuites,
MinVersion: tls.VersionTLS12,
}
}
// TLSReloader updates the TLS certificate without restarting the server.
type TLSReloader struct {
certMu sync.RWMutex
cert *tls.Certificate
}
// NewTLSReloader is used to create a new TLS Reloader that will be used
// to update the TLS certificate without restarting the server.
func NewTLSReloader(certBytes, keyBytes []byte) (*TLSReloader, error) {
cert, _, err := LoadCertFromBytes(certBytes, keyBytes)
if err != nil {
return nil, err
}
return &TLSReloader{
cert: &cert,
}, nil
}
// AttemptReload will make an attempt to update the TLS certificate
// and key used by the server.
func (t *TLSReloader) AttemptReload(certBytes, keyBytes []byte) error {
newCert, _, err := LoadCertFromBytes(certBytes, keyBytes)
if err != nil {
return err
}
t.certMu.Lock()
t.cert = &newCert
t.certMu.Unlock()
return nil
}
// GetCertificateFunc is used in the server's TLS configuration to
// determine the correct TLS certificate to server on a request.
func (t *TLSReloader) GetCertificateFunc() func(*tls.ClientHelloInfo) (
*tls.Certificate, error) {
return func(clientHello *tls.ClientHelloInfo) (*tls.Certificate,
error) {
t.certMu.RLock()
defer t.certMu.RUnlock()
return t.cert, nil
}
}