2023-03-08 17:18:46 +08:00
package tls
import (
"bytes"
"context"
"crypto/aes"
"crypto/cipher"
2023-12-09 12:28:19 +08:00
"crypto/ecdh"
2023-03-08 17:18:46 +08:00
"crypto/ed25519"
"crypto/hmac"
"crypto/sha256"
"crypto/sha512"
"crypto/tls"
"crypto/x509"
"encoding/binary"
"errors"
"net"
"net/http"
"strings"
"time"
2023-11-03 21:01:45 +08:00
"github.com/metacubex/mihomo/log"
"github.com/metacubex/mihomo/ntp"
2023-03-08 17:18:46 +08:00
2024-05-31 11:31:17 +08:00
"github.com/metacubex/randv2"
2024-05-19 11:31:37 +08:00
utls "github.com/metacubex/utls"
2023-06-14 17:17:46 +08:00
"golang.org/x/crypto/chacha20poly1305"
2023-03-08 17:18:46 +08:00
"golang.org/x/crypto/hkdf"
"golang.org/x/net/http2"
)
2023-03-11 12:23:27 +08:00
const RealityMaxShortIDLen = 8
2023-03-08 17:18:46 +08:00
type RealityConfig struct {
2023-12-09 15:43:40 +08:00
PublicKey * ecdh . PublicKey
2023-03-11 12:23:27 +08:00
ShortID [ RealityMaxShortIDLen ] byte
2025-05-15 10:14:18 +08:00
SupportX25519MLKEM768 bool
2023-03-08 17:18:46 +08:00
}
2025-05-17 13:53:21 +08:00
func GetRealityConn ( ctx context . Context , conn net . Conn , fingerprint UClientHelloID , tlsConfig * Config , realityConfig * RealityConfig ) ( net . Conn , error ) {
2025-04-29 21:15:48 +08:00
for retry := 0 ; ; retry ++ {
2023-03-08 17:18:46 +08:00
verifier := & realityVerifier {
2023-03-08 20:28:12 +08:00
serverName : tlsConfig . ServerName ,
}
uConfig := & utls . Config {
ServerName : tlsConfig . ServerName ,
InsecureSkipVerify : true ,
SessionTicketsDisabled : true ,
VerifyPeerCertificate : verifier . VerifyPeerCertificate ,
2023-03-08 17:18:46 +08:00
}
2025-05-15 10:14:18 +08:00
2025-05-18 00:49:15 +08:00
if ! realityConfig . SupportX25519MLKEM768 && fingerprint == HelloChrome_Auto {
fingerprint = HelloChrome_120 // old reality server doesn't work with X25519MLKEM768
}
2025-05-15 10:14:18 +08:00
uConn := utls . UClient ( conn , uConfig , fingerprint )
2023-03-08 17:18:46 +08:00
verifier . UConn = uConn
err := uConn . BuildHandshakeState ( )
if err != nil {
return nil , err
}
hello := uConn . HandshakeState . Hello
2023-06-14 17:17:46 +08:00
rawSessionID := hello . Raw [ 39 : 39 + 32 ] // the location of session ID
for i := range rawSessionID { // https://github.com/golang/go/issues/5373
rawSessionID [ i ] = 0
2023-03-15 15:55:18 +08:00
}
2023-03-08 17:18:46 +08:00
2023-09-01 03:11:35 +08:00
binary . BigEndian . PutUint64 ( hello . SessionId , uint64 ( ntp . Now ( ) . Unix ( ) ) )
2023-03-08 17:18:46 +08:00
2023-06-14 17:17:46 +08:00
copy ( hello . SessionId [ 8 : ] , realityConfig . ShortID [ : ] )
2023-03-08 17:18:46 +08:00
hello . SessionId [ 0 ] = 1
2023-03-22 23:45:26 +08:00
hello . SessionId [ 1 ] = 8
2023-06-14 17:17:46 +08:00
hello . SessionId [ 2 ] = 2
2023-03-08 17:18:46 +08:00
//log.Debugln("REALITY hello.sessionId[:16]: %v", hello.SessionId[:16])
2025-03-18 09:09:54 +08:00
keyShareKeys := uConn . HandshakeState . State13 . KeyShareKeys
if keyShareKeys == nil {
// WTF???
if retry > 2 {
return nil , errors . New ( "nil keyShareKeys" )
}
continue // retry
}
ecdheKey := keyShareKeys . Ecdhe
2025-07-22 15:00:25 +08:00
if ecdheKey == nil {
ecdheKey = keyShareKeys . MlkemEcdhe
}
2023-12-09 12:28:19 +08:00
if ecdheKey == nil {
2023-10-23 23:33:44 +08:00
// WTF???
if retry > 2 {
2023-12-09 12:28:19 +08:00
return nil , errors . New ( "nil ecdheKey" )
2023-10-23 23:33:44 +08:00
}
continue // retry
}
2023-12-09 15:43:40 +08:00
authKey , err := ecdheKey . ECDH ( realityConfig . PublicKey )
2023-12-09 12:28:19 +08:00
if err != nil {
return nil , err
}
2023-03-08 17:18:46 +08:00
if authKey == nil {
return nil , errors . New ( "nil auth_key" )
}
verifier . authKey = authKey
_ , err = hkdf . New ( sha256 . New , authKey , hello . Random [ : 20 ] , [ ] byte ( "REALITY" ) ) . Read ( authKey )
if err != nil {
return nil , err
}
2023-06-14 17:17:46 +08:00
var aeadCipher cipher . AEAD
2024-05-19 11:31:37 +08:00
if utls . AesgcmPreferred ( hello . CipherSuites ) {
2023-06-14 17:17:46 +08:00
aesBlock , _ := aes . NewCipher ( authKey )
aeadCipher , _ = cipher . NewGCM ( aesBlock )
} else {
aeadCipher , _ = chacha20poly1305 . New ( authKey )
}
aeadCipher . Seal ( hello . SessionId [ : 0 ] , hello . Random [ 20 : ] , hello . SessionId [ : 16 ] , hello . Raw )
2023-03-08 17:18:46 +08:00
copy ( hello . Raw [ 39 : ] , hello . SessionId )
//log.Debugln("REALITY hello.sessionId: %v", hello.SessionId)
//log.Debugln("REALITY uConn.AuthKey: %v", authKey)
err = uConn . HandshakeContext ( ctx )
if err != nil {
return nil , err
}
2023-06-14 17:17:46 +08:00
log . Debugln ( "REALITY Authentication: %v, AEAD: %T" , verifier . verified , aeadCipher )
2023-03-08 17:18:46 +08:00
if ! verifier . verified {
2025-05-15 10:14:18 +08:00
go realityClientFallback ( uConn , uConfig . ServerName , fingerprint )
2023-03-08 17:18:46 +08:00
return nil , errors . New ( "REALITY authentication failed" )
}
return uConn , nil
}
}
func realityClientFallback ( uConn net . Conn , serverName string , fingerprint utls . ClientHelloID ) {
defer uConn . Close ( )
2023-03-11 12:23:27 +08:00
client := http . Client {
2023-03-08 17:18:46 +08:00
Transport : & http2 . Transport {
DialTLSContext : func ( ctx context . Context , network , addr string , config * tls . Config ) ( net . Conn , error ) {
return uConn , nil
} ,
} ,
}
2024-05-15 13:53:18 +08:00
request , err := http . NewRequest ( "GET" , "https://" + serverName , nil )
if err != nil {
return
}
2023-03-08 17:18:46 +08:00
request . Header . Set ( "User-Agent" , fingerprint . Client )
2024-05-31 11:31:17 +08:00
request . AddCookie ( & http . Cookie { Name : "padding" , Value : strings . Repeat ( "0" , randv2 . IntN ( 32 ) + 30 ) } )
2023-03-08 17:18:46 +08:00
response , err := client . Do ( request )
if err != nil {
return
}
//_, _ = io.Copy(io.Discard, response.Body)
2024-05-31 11:31:17 +08:00
time . Sleep ( time . Duration ( 5 + randv2 . IntN ( 10 ) ) * time . Second )
2023-03-08 17:18:46 +08:00
response . Body . Close ( )
client . CloseIdleConnections ( )
}
type realityVerifier struct {
* utls . UConn
serverName string
authKey [ ] byte
verified bool
}
2024-05-19 11:31:37 +08:00
//var pOffset = utils.MustOK(reflect.TypeOf((*utls.Conn)(nil)).Elem().FieldByName("peerCertificates")).Offset
2023-03-11 12:23:27 +08:00
2023-03-08 17:18:46 +08:00
func ( c * realityVerifier ) VerifyPeerCertificate ( rawCerts [ ] [ ] byte , verifiedChains [ ] [ ] * x509 . Certificate ) error {
2025-07-22 15:00:25 +08:00
//log.Debugln("REALITY localAddr: %v\t is using X25519MLKEM768 for TLS' communication: %v", c.RemoteAddr(), c.HandshakeState.ServerHello.SelectedGroup == utls.X25519MLKEM768)
2023-03-11 12:23:27 +08:00
//p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates")
2024-05-19 11:31:37 +08:00
//certs := *(*[]*x509.Certificate)(unsafe.Add(unsafe.Pointer(c.Conn), pOffset))
certs := c . Conn . PeerCertificates ( )
2023-03-08 17:18:46 +08:00
if pub , ok := certs [ 0 ] . PublicKey . ( ed25519 . PublicKey ) ; ok {
h := hmac . New ( sha512 . New , c . authKey )
h . Write ( pub )
if bytes . Equal ( h . Sum ( nil ) , certs [ 0 ] . Signature ) {
c . verified = true
return nil
}
}
opts := x509 . VerifyOptions {
DNSName : c . serverName ,
Intermediates : x509 . NewCertPool ( ) ,
}
for _ , cert := range certs [ 1 : ] {
opts . Intermediates . AddCert ( cert )
}
if _ , err := certs [ 0 ] . Verify ( opts ) ; err != nil {
return err
}
return nil
}