mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2025-04-29 22:55:06 +03:00
126 lines
3.3 KiB
Go
126 lines
3.3 KiB
Go
package core
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/ed25519"
|
|
"crypto/rand"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/hex"
|
|
"encoding/pem"
|
|
"errors"
|
|
"log"
|
|
"math/big"
|
|
"net"
|
|
"time"
|
|
)
|
|
|
|
type tcptls struct {
|
|
tcp *tcp
|
|
config *tls.Config
|
|
forDialer *TcpUpgrade
|
|
forListener *TcpUpgrade
|
|
}
|
|
|
|
func (t *tcptls) init(tcp *tcp) {
|
|
t.tcp = tcp
|
|
t.forDialer = &TcpUpgrade{
|
|
upgrade: t.upgradeDialer,
|
|
name: "tls",
|
|
}
|
|
t.forListener = &TcpUpgrade{
|
|
upgrade: t.upgradeListener,
|
|
name: "tls",
|
|
}
|
|
|
|
edpriv := make(ed25519.PrivateKey, ed25519.PrivateKeySize)
|
|
copy(edpriv[:], tcp.links.core.secret[:])
|
|
|
|
certBuf := &bytes.Buffer{}
|
|
|
|
// TODO: because NotAfter is finite, we should add some mechanism to regenerate the certificate and restart the listeners periodically for nodes with very high uptimes. Perhaps regenerate certs and restart listeners every few months or so.
|
|
pubtemp := x509.Certificate{
|
|
SerialNumber: big.NewInt(1),
|
|
Subject: pkix.Name{
|
|
CommonName: hex.EncodeToString(tcp.links.core.public[:]),
|
|
},
|
|
NotBefore: time.Now(),
|
|
NotAfter: time.Now().Add(time.Hour * 24 * 365),
|
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
|
BasicConstraintsValid: true,
|
|
}
|
|
|
|
derbytes, err := x509.CreateCertificate(rand.Reader, &pubtemp, &pubtemp, edpriv.Public(), edpriv)
|
|
if err != nil {
|
|
log.Fatalf("Failed to create certificate: %s", err)
|
|
}
|
|
|
|
if err := pem.Encode(certBuf, &pem.Block{Type: "CERTIFICATE", Bytes: derbytes}); err != nil {
|
|
panic("failed to encode certificate into PEM")
|
|
}
|
|
|
|
cpool := x509.NewCertPool()
|
|
cpool.AppendCertsFromPEM(derbytes)
|
|
|
|
t.config = &tls.Config{
|
|
RootCAs: cpool,
|
|
Certificates: []tls.Certificate{
|
|
{
|
|
Certificate: [][]byte{derbytes},
|
|
PrivateKey: edpriv,
|
|
},
|
|
},
|
|
InsecureSkipVerify: true,
|
|
MinVersion: tls.VersionTLS13,
|
|
}
|
|
}
|
|
|
|
func (t *tcptls) configForOptions(options *tcpOptions) *tls.Config {
|
|
config := t.config.Clone()
|
|
config.VerifyPeerCertificate = func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
|
|
if len(rawCerts) != 1 {
|
|
return errors.New("tls not exactly 1 cert")
|
|
}
|
|
cert, err := x509.ParseCertificate(rawCerts[0])
|
|
if err != nil {
|
|
return errors.New("tls failed to parse cert")
|
|
}
|
|
if cert.PublicKeyAlgorithm != x509.Ed25519 {
|
|
return errors.New("tls wrong cert algorithm")
|
|
}
|
|
pk := cert.PublicKey.(ed25519.PublicKey)
|
|
var key keyArray
|
|
copy(key[:], pk)
|
|
// If options does not have a pinned key, then pin one now
|
|
if options.pinnedEd25519Keys == nil {
|
|
options.pinnedEd25519Keys = make(map[keyArray]struct{})
|
|
options.pinnedEd25519Keys[key] = struct{}{}
|
|
}
|
|
if _, isIn := options.pinnedEd25519Keys[key]; !isIn {
|
|
return errors.New("tls key does not match pinned key")
|
|
}
|
|
return nil
|
|
}
|
|
return config
|
|
}
|
|
|
|
func (t *tcptls) upgradeListener(c net.Conn, options *tcpOptions) (net.Conn, error) {
|
|
config := t.configForOptions(options)
|
|
conn := tls.Server(c, config)
|
|
if err := conn.Handshake(); err != nil {
|
|
return c, err
|
|
}
|
|
return conn, nil
|
|
}
|
|
|
|
func (t *tcptls) upgradeDialer(c net.Conn, options *tcpOptions) (net.Conn, error) {
|
|
config := t.configForOptions(options)
|
|
config.ServerName = options.tlsSNI
|
|
conn := tls.Client(c, config)
|
|
if err := conn.Handshake(); err != nil {
|
|
return c, err
|
|
}
|
|
return conn, nil
|
|
}
|