package iroh
import (
"crypto/ed25519"
"encoding/base32"
"errors"
"fmt"
"strings"
tls "github.com/tmc/go-iroh/internal/itls/tls"
"github.com/tmc/go-iroh/key"
)
var base32DNSSEC = base32 .NewEncoding ("0123456789abcdefghijklmnopqrstuv" ).WithPadding (base32 .NoPadding )
const tlsNameSuffix = ".iroh.invalid"
type KeyExchangePolicy uint8
const (
KeyExchangeDefault KeyExchangePolicy = iota
KeyExchangeClassical
KeyExchangePreferPQ
KeyExchangePQOnly
)
func (p KeyExchangePolicy ) valid () bool { return p <= KeyExchangePQOnly }
func (p KeyExchangePolicy ) curves () []tls .CurveID {
switch p {
case KeyExchangeClassical :
return []tls .CurveID {tls .X25519 , tls .CurveP256 , tls .CurveP384 , tls .CurveP521 }
case KeyExchangePreferPQ :
return []tls .CurveID {tls .X25519MLKEM768 , tls .X25519 , tls .CurveP256 , tls .CurveP384 }
case KeyExchangePQOnly :
return []tls .CurveID {tls .X25519MLKEM768 }
default :
return nil
}
}
func ServerName (id key .EndpointID ) string {
b := id .Bytes ()
return base32DNSSEC .EncodeToString (b [:]) + tlsNameSuffix
}
func endpointIDFromServerName(name string ) (key .EndpointID , bool ) {
rest , ok := strings .CutSuffix (name , tlsNameSuffix )
if !ok || strings .Contains (rest , "." ) {
return key .EndpointID {}, false
}
raw , err := base32DNSSEC .DecodeString (rest )
if err != nil || len (raw ) != key .PublicKeySize {
return key .EndpointID {}, false
}
id , err := key .EndpointIDFromSlice (raw )
if err != nil {
return key .EndpointID {}, false
}
return id , true
}
func rawKeyCertificate(sk key .SecretKey ) (tls .Certificate , error ) {
priv := sk .Ed25519 ()
pub := priv .Public ().(ed25519 .PublicKey )
return tls .MarshalRawPublicKeyCertificate (pub , priv )
}
func peerEndpointID(cs tls .ConnectionState ) (key .EndpointID , error ) {
if len (cs .PeerCertificates ) != 1 {
return key .EndpointID {}, fmt .Errorf ("iroh: expected exactly one peer certificate, got %d" , len (cs .PeerCertificates ))
}
pub , ok := cs .PeerCertificates [0 ].PublicKey .(ed25519 .PublicKey )
if !ok {
return key .EndpointID {}, errors .New ("iroh: peer key is not ed25519" )
}
id , err := key .EndpointIDFromSlice (pub )
if err != nil {
return key .EndpointID {}, fmt .Errorf ("iroh: peer key: %w" , err )
}
return id , nil
}
func clientTLSConfig(sk key .SecretKey , want key .EndpointID , alpns []string , cache tls .ClientSessionCache ) (*tls .Config , error ) {
return clientTLSConfigWithCurves (sk , want , alpns , cache , nil )
}
func clientTLSConfigWithCurves(sk key .SecretKey , want key .EndpointID , alpns []string , cache tls .ClientSessionCache , curves []tls .CurveID ) (*tls .Config , error ) {
cert , err := rawKeyCertificate (sk )
if err != nil {
return nil , err
}
return &tls .Config {
Certificates : []tls .Certificate {cert },
RawPublicKeys : true ,
MinVersion : tls .VersionTLS13 ,
MaxVersion : tls .VersionTLS13 ,
SessionTicketsDisabled : cache == nil ,
ClientSessionCache : cache ,
CurvePreferences : curves ,
NextProtos : alpns ,
ServerName : ServerName (want ),
InsecureSkipVerify : true ,
VerifyConnection : func (cs tls .ConnectionState ) error {
got , err := peerEndpointID (cs )
if err != nil {
return err
}
if !got .Equal (want ) {
return fmt .Errorf ("iroh: server identity mismatch: dialed %s, got %s" , want , got )
}
return nil
},
}, nil
}
func serverTLSConfig(sk key .SecretKey , alpns []string ) (*tls .Config , error ) {
return serverTLSConfigWithCurves (sk , alpns , nil )
}
func serverTLSConfigWithCurves(sk key .SecretKey , alpns []string , curves []tls .CurveID ) (*tls .Config , error ) {
cert , err := rawKeyCertificate (sk )
if err != nil {
return nil , err
}
return &tls .Config {
Certificates : []tls .Certificate {cert },
RawPublicKeys : true ,
MinVersion : tls .VersionTLS13 ,
MaxVersion : tls .VersionTLS13 ,
SessionTicketsDisabled : false ,
CurvePreferences : curves ,
NextProtos : alpns ,
ClientAuth : tls .RequireAnyClientCert ,
InsecureSkipVerify : true ,
VerifyConnection : func (cs tls .ConnectionState ) error {
_ , err := peerEndpointID (cs )
return err
},
}, nil
}
The pages are generated with Golds v0.8.4 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .