uquic/u_parrot.go
Gaukas Wang 9b03bc282c
fix: uquic sync error
...so that uquic build/test shall pass and examples shall work again.
2023-08-28 16:53:19 -06:00

686 lines
19 KiB
Go

package quic
import (
"crypto/rand"
"fmt"
"math/big"
mrand "math/rand"
tls "github.com/refraction-networking/utls"
)
type QUICID struct {
Client string
// Version specifies version of a mimicked clients (e.g. browsers).
Version string
// Fingerprint is a unique identifier for each different QUIC client/spec.
Fingerprint string
}
const (
// clients
quicFirefox = "Firefox"
quicChrome = "Chrome"
quicIOS = "iOS"
quicAndroid = "Android"
quicEdge = "Edge"
quicSafari = "Safari"
)
var (
QUICFirefox_116 = QUICFirefox_116A // point to most-popular 8-byte DCID
QUICFirefox_116A = QUICID{quicFirefox, "116", "31ea0e4ffd75b477"} // DCID.len = 8
QUICFirefox_116B = QUICID{quicFirefox, "116", "d07d3c9152fbc5e0"} // DCID.len = 9
QUICFirefox_116C = QUICID{quicFirefox, "116", "c74f87b2a9ccc006"} // DCID.len = 15
// TODO: add Firefox fingerprints with Token and PSK extension
QUICChrome_115 = QUICChrome_115_IPv4 // IPv4 is still more popular
QUICChrome_115_IPv4 = QUICID{quicChrome, "115", "beeb454235791d5c"} // IPv4: UDP payload 20-byte longer than IPv6 due to padding
QUICChrome_115_IPv6 = QUICID{quicChrome, "115_ip6", "beeb454235791d5c"} // IPv6
// TODO: add Chrome fingerprints with Token and PSK extension
// TODO: add more QUIC clients and versions
)
func QUICID2Spec(id QUICID) (QUICSpec, error) {
switch id {
case QUICChrome_115_IPv4:
return QUICSpec{
InitialPacketSpec: InitialPacketSpec{
SrcConnIDLength: 0,
DestConnIDLength: 8,
InitPacketNumberLength: 1,
InitPacketNumber: 1, // Chrome is special that it starts with 1 not 0
ClientTokenLength: 0,
FrameBuilder: &QUICRandomFrames{ // Chrome randomly inserts padding frames
MinPING: 0,
MaxPING: 10,
MinCRYPTO: 1,
MaxCRYPTO: 10,
MinPADDING: 3,
MaxPADDING: 6,
Length: 1231 - 16, // 16-byte for Auth Tag
},
},
ClientHelloSpec: &tls.ClientHelloSpec{
TLSVersMin: tls.VersionTLS13,
TLSVersMax: tls.VersionTLS13,
CipherSuites: []uint16{
tls.TLS_AES_128_GCM_SHA256,
tls.TLS_AES_256_GCM_SHA384,
tls.TLS_CHACHA20_POLY1305_SHA256,
},
CompressionMethods: []uint8{
0x0, // no compression
},
Extensions: tls.ShuffleChromeTLSExtensions([]tls.TLSExtension{
ShuffleQUICTransportParameters(&tls.QUICTransportParametersExtension{ // Order of QTPs are always shuffled
TransportParameters: tls.TransportParameters{
tls.InitialMaxStreamsUni(103),
tls.MaxIdleTimeout(30000),
tls.InitialMaxData(15728640),
tls.InitialMaxStreamDataUni(6291456),
&tls.VersionInformation{
ChoosenVersion: tls.VERSION_1,
AvailableVersions: []uint32{
tls.VERSION_GREASE,
tls.VERSION_1,
},
LegacyID: true,
},
&tls.FakeQUICTransportParameter{ // google_quic_version
Id: 0x4752,
Val: []byte{00, 00, 00, 01}, // Google QUIC version 1
},
&tls.FakeQUICTransportParameter{ // google_connection_options
Id: 0x3128,
Val: []byte{0x52, 0x56, 0x43, 0x4d},
},
tls.MaxDatagramFrameSize(65536),
tls.InitialMaxStreamsBidi(100),
tls.InitialMaxStreamDataBidiLocal(6291456),
VariableLengthGREASEQTP(0x10), // Random length for GREASE QTP
tls.InitialSourceConnectionID([]byte{}),
tls.MaxUDPPayloadSize(1472),
tls.InitialMaxStreamDataBidiRemote(6291456),
},
}),
&tls.ApplicationSettingsExtension{
SupportedProtocols: []string{
"h3",
},
},
&tls.UtlsCompressCertExtension{
Algorithms: []tls.CertCompressionAlgo{
tls.CertCompressionBrotli,
},
},
&tls.KeyShareExtension{
KeyShares: []tls.KeyShare{
{
Group: tls.X25519,
},
},
},
&tls.SignatureAlgorithmsExtension{
SupportedSignatureAlgorithms: []tls.SignatureScheme{
tls.ECDSAWithP256AndSHA256,
tls.PSSWithSHA256,
tls.PKCS1WithSHA256,
tls.ECDSAWithP384AndSHA384,
tls.PSSWithSHA384,
tls.PKCS1WithSHA384,
tls.PSSWithSHA512,
tls.PKCS1WithSHA512,
tls.PKCS1WithSHA1,
},
},
&tls.SNIExtension{},
&tls.SupportedCurvesExtension{
Curves: []tls.CurveID{
tls.CurveX25519,
tls.CurveSECP256R1,
tls.CurveSECP384R1,
},
},
&tls.PSKKeyExchangeModesExtension{
Modes: []uint8{
tls.PskModeDHE,
},
},
&tls.ALPNExtension{
AlpnProtocols: []string{
"h3",
},
},
&tls.SupportedVersionsExtension{
Versions: []uint16{
tls.VersionTLS13,
},
},
}),
},
}, nil
case QUICChrome_115_IPv6:
return QUICSpec{
InitialPacketSpec: InitialPacketSpec{
SrcConnIDLength: 0,
DestConnIDLength: 8,
InitPacketNumberLength: 1,
InitPacketNumber: 1, // Chrome is special that it starts with 1 not 0
ClientTokenLength: 0,
FrameBuilder: &QUICRandomFrames{ // Chrome randomly inserts padding frames
MinPING: 0,
MaxPING: 10,
MinCRYPTO: 1,
MaxCRYPTO: 10,
MinPADDING: 3,
MaxPADDING: 6,
Length: 1211 - 16, // IPv6 pads to a length that is 20-byte shorter than IPv4's version
},
},
ClientHelloSpec: &tls.ClientHelloSpec{
TLSVersMin: tls.VersionTLS13,
TLSVersMax: tls.VersionTLS13,
CipherSuites: []uint16{
tls.TLS_AES_128_GCM_SHA256,
tls.TLS_AES_256_GCM_SHA384,
tls.TLS_CHACHA20_POLY1305_SHA256,
},
CompressionMethods: []uint8{
0x0,
},
Extensions: tls.ShuffleChromeTLSExtensions([]tls.TLSExtension{
ShuffleQUICTransportParameters(&tls.QUICTransportParametersExtension{ // Order of QTPs are always shuffled
TransportParameters: tls.TransportParameters{
tls.InitialMaxStreamsUni(103),
tls.MaxIdleTimeout(30000),
tls.InitialMaxData(15728640),
tls.InitialMaxStreamDataUni(6291456),
&tls.VersionInformation{
ChoosenVersion: tls.VERSION_1,
AvailableVersions: []uint32{
tls.VERSION_GREASE,
tls.VERSION_1,
},
LegacyID: true,
},
&tls.FakeQUICTransportParameter{ // google_quic_version
Id: 0x4752,
Val: []byte{00, 00, 00, 01}, // Google QUIC version 1
},
&tls.FakeQUICTransportParameter{ // google_connection_options
Id: 0x3128,
Val: []byte{0x52, 0x56, 0x43, 0x4d},
},
tls.MaxDatagramFrameSize(65536),
tls.InitialMaxStreamsBidi(100),
tls.InitialMaxStreamDataBidiLocal(6291456),
VariableLengthGREASEQTP(0x10), // Random length for GREASE QTP
tls.InitialSourceConnectionID([]byte{}),
tls.MaxUDPPayloadSize(1472),
tls.InitialMaxStreamDataBidiRemote(6291456),
},
}),
&tls.ApplicationSettingsExtension{
SupportedProtocols: []string{
"h3",
},
},
&tls.UtlsCompressCertExtension{
Algorithms: []tls.CertCompressionAlgo{
tls.CertCompressionBrotli,
},
},
&tls.KeyShareExtension{
KeyShares: []tls.KeyShare{
{
Group: tls.X25519,
},
},
},
&tls.SignatureAlgorithmsExtension{
SupportedSignatureAlgorithms: []tls.SignatureScheme{
tls.ECDSAWithP256AndSHA256,
tls.PSSWithSHA256,
tls.PKCS1WithSHA256,
tls.ECDSAWithP384AndSHA384,
tls.PSSWithSHA384,
tls.PKCS1WithSHA384,
tls.PSSWithSHA512,
tls.PKCS1WithSHA512,
tls.PKCS1WithSHA1,
},
},
&tls.SNIExtension{},
&tls.SupportedCurvesExtension{
Curves: []tls.CurveID{
tls.CurveX25519,
tls.CurveSECP256R1,
tls.CurveSECP384R1,
},
},
&tls.PSKKeyExchangeModesExtension{
Modes: []uint8{
tls.PskModeDHE,
},
},
&tls.ALPNExtension{
AlpnProtocols: []string{
"h3",
},
},
&tls.SupportedVersionsExtension{
Versions: []uint16{
tls.VersionTLS13,
},
},
}),
},
}, nil
case QUICFirefox_116A:
return QUICSpec{
InitialPacketSpec: InitialPacketSpec{
SrcConnIDLength: 3,
DestConnIDLength: 8,
InitPacketNumberLength: 1,
InitPacketNumber: 0,
ClientTokenLength: 0,
FrameBuilder: QUICFrames{}, // empty = single crypto
},
ClientHelloSpec: &tls.ClientHelloSpec{
TLSVersMin: tls.VersionTLS13,
TLSVersMax: tls.VersionTLS13,
CipherSuites: []uint16{
tls.TLS_AES_128_GCM_SHA256,
tls.TLS_CHACHA20_POLY1305_SHA256,
tls.TLS_AES_256_GCM_SHA384,
},
CompressionMethods: []uint8{
0x0,
},
Extensions: []tls.TLSExtension{
&tls.SNIExtension{},
&tls.ExtendedMasterSecretExtension{},
&tls.RenegotiationInfoExtension{
Renegotiation: tls.RenegotiateOnceAsClient,
},
&tls.SupportedCurvesExtension{
Curves: []tls.CurveID{
tls.CurveX25519,
tls.CurveSECP256R1,
tls.CurveSECP384R1,
tls.CurveSECP521R1,
tls.FakeCurveFFDHE2048,
tls.FakeCurveFFDHE3072,
tls.FakeCurveFFDHE4096,
tls.FakeCurveFFDHE6144,
tls.FakeCurveFFDHE8192,
},
},
&tls.ALPNExtension{
AlpnProtocols: []string{
"h3",
},
},
&tls.StatusRequestExtension{},
&tls.FakeDelegatedCredentialsExtension{
SupportedSignatureAlgorithms: []tls.SignatureScheme{
tls.ECDSAWithP256AndSHA256,
tls.ECDSAWithP384AndSHA384,
tls.ECDSAWithP521AndSHA512,
tls.ECDSAWithSHA1,
},
},
&tls.KeyShareExtension{
KeyShares: []tls.KeyShare{
{
Group: tls.X25519,
},
},
},
&tls.SupportedVersionsExtension{
Versions: []uint16{
tls.VersionTLS13,
},
},
&tls.SignatureAlgorithmsExtension{
SupportedSignatureAlgorithms: []tls.SignatureScheme{
tls.ECDSAWithP256AndSHA256,
tls.ECDSAWithP384AndSHA384,
tls.ECDSAWithP521AndSHA512,
tls.ECDSAWithSHA1,
tls.PSSWithSHA256,
tls.PSSWithSHA384,
tls.PSSWithSHA512,
tls.PKCS1WithSHA256,
tls.PKCS1WithSHA384,
tls.PKCS1WithSHA512,
tls.PKCS1WithSHA1,
},
},
&tls.PSKKeyExchangeModesExtension{
Modes: []uint8{
tls.PskModeDHE,
},
},
&tls.FakeRecordSizeLimitExtension{
Limit: 0x4001,
},
ShuffleQUICTransportParameters(&tls.QUICTransportParametersExtension{
TransportParameters: tls.TransportParameters{
tls.InitialMaxStreamDataBidiRemote(0x100000),
tls.InitialMaxStreamsBidi(16),
tls.MaxDatagramFrameSize(1200),
tls.MaxIdleTimeout(30000),
tls.ActiveConnectionIDLimit(8),
&tls.GREASEQUICBit{},
&tls.VersionInformation{
ChoosenVersion: tls.VERSION_1,
AvailableVersions: []uint32{
tls.VERSION_GREASE,
tls.VERSION_1,
},
LegacyID: true,
},
tls.InitialMaxStreamsUni(16),
&tls.GREASETransportParameter{
Length: 2, // Firefox uses 2-byte GREASE values
},
tls.InitialMaxStreamDataBidiLocal(0xc00000),
tls.InitialMaxStreamDataUni(0x100000),
tls.InitialSourceConnectionID([]byte{}),
tls.MaxAckDelay(20),
tls.InitialMaxData(0x1800000),
&tls.DisableActiveMigration{},
},
}),
&tls.UtlsPaddingExtension{
GetPaddingLen: tls.BoringPaddingStyle,
},
},
},
UDPDatagramMinSize: 1357, // Firefox pads with zeroes at the end of UDP datagrams
}, nil
case QUICFirefox_116B:
return QUICSpec{
InitialPacketSpec: InitialPacketSpec{
SrcConnIDLength: 3,
DestConnIDLength: 9,
InitPacketNumberLength: 1,
InitPacketNumber: 0,
ClientTokenLength: 0,
FrameBuilder: QUICFrames{},
},
ClientHelloSpec: &tls.ClientHelloSpec{
TLSVersMin: tls.VersionTLS13,
TLSVersMax: tls.VersionTLS13,
CipherSuites: []uint16{
tls.TLS_AES_128_GCM_SHA256,
tls.TLS_CHACHA20_POLY1305_SHA256,
tls.TLS_AES_256_GCM_SHA384,
},
CompressionMethods: []uint8{
0x0,
},
Extensions: []tls.TLSExtension{
&tls.SNIExtension{},
&tls.ExtendedMasterSecretExtension{},
&tls.RenegotiationInfoExtension{
Renegotiation: tls.RenegotiateOnceAsClient,
},
&tls.SupportedCurvesExtension{
Curves: []tls.CurveID{
tls.CurveX25519,
tls.CurveSECP256R1,
tls.CurveSECP384R1,
tls.CurveSECP521R1,
tls.FakeCurveFFDHE2048,
tls.FakeCurveFFDHE3072,
tls.FakeCurveFFDHE4096,
tls.FakeCurveFFDHE6144,
tls.FakeCurveFFDHE8192,
},
},
&tls.ALPNExtension{
AlpnProtocols: []string{
"h3",
},
},
&tls.StatusRequestExtension{},
&tls.FakeDelegatedCredentialsExtension{
SupportedSignatureAlgorithms: []tls.SignatureScheme{
tls.ECDSAWithP256AndSHA256,
tls.ECDSAWithP384AndSHA384,
tls.ECDSAWithP521AndSHA512,
tls.ECDSAWithSHA1,
},
},
&tls.KeyShareExtension{
KeyShares: []tls.KeyShare{
{
Group: tls.X25519,
},
},
},
&tls.SupportedVersionsExtension{
Versions: []uint16{
tls.VersionTLS13,
},
},
&tls.SignatureAlgorithmsExtension{
SupportedSignatureAlgorithms: []tls.SignatureScheme{
tls.ECDSAWithP256AndSHA256,
tls.ECDSAWithP384AndSHA384,
tls.ECDSAWithP521AndSHA512,
tls.ECDSAWithSHA1,
tls.PSSWithSHA256,
tls.PSSWithSHA384,
tls.PSSWithSHA512,
tls.PKCS1WithSHA256,
tls.PKCS1WithSHA384,
tls.PKCS1WithSHA512,
tls.PKCS1WithSHA1,
},
},
&tls.PSKKeyExchangeModesExtension{
Modes: []uint8{
tls.PskModeDHE,
},
},
&tls.FakeRecordSizeLimitExtension{
Limit: 0x4001,
},
ShuffleQUICTransportParameters(&tls.QUICTransportParametersExtension{
TransportParameters: tls.TransportParameters{
tls.InitialMaxStreamDataBidiRemote(0x100000),
tls.InitialMaxStreamsBidi(16),
tls.MaxDatagramFrameSize(1200),
tls.MaxIdleTimeout(30000),
tls.ActiveConnectionIDLimit(8),
&tls.GREASEQUICBit{},
&tls.VersionInformation{
ChoosenVersion: tls.VERSION_1,
AvailableVersions: []uint32{
tls.VERSION_GREASE,
tls.VERSION_1,
},
LegacyID: true,
},
tls.InitialMaxStreamsUni(16),
&tls.GREASETransportParameter{
Length: 2, // Firefox uses 2-byte GREASE values
},
tls.InitialMaxStreamDataBidiLocal(0xc00000),
tls.InitialMaxStreamDataUni(0x100000),
tls.InitialSourceConnectionID([]byte{}),
tls.MaxAckDelay(20),
tls.InitialMaxData(0x1800000),
&tls.DisableActiveMigration{},
},
}),
&tls.UtlsPaddingExtension{
GetPaddingLen: tls.BoringPaddingStyle,
},
},
},
UDPDatagramMinSize: 1357,
}, nil
case QUICFirefox_116C:
return QUICSpec{
InitialPacketSpec: InitialPacketSpec{
SrcConnIDLength: 3,
DestConnIDLength: 15,
InitPacketNumberLength: 1,
InitPacketNumber: 0,
ClientTokenLength: 0,
FrameBuilder: QUICFrames{},
},
ClientHelloSpec: &tls.ClientHelloSpec{
TLSVersMin: tls.VersionTLS13,
TLSVersMax: tls.VersionTLS13,
CipherSuites: []uint16{
tls.TLS_AES_128_GCM_SHA256,
tls.TLS_CHACHA20_POLY1305_SHA256,
tls.TLS_AES_256_GCM_SHA384,
},
CompressionMethods: []uint8{
0x0,
},
Extensions: []tls.TLSExtension{
&tls.SNIExtension{},
&tls.ExtendedMasterSecretExtension{},
&tls.RenegotiationInfoExtension{
Renegotiation: tls.RenegotiateOnceAsClient,
},
&tls.SupportedCurvesExtension{
Curves: []tls.CurveID{
tls.CurveX25519,
tls.CurveSECP256R1,
tls.CurveSECP384R1,
tls.CurveSECP521R1,
tls.FakeCurveFFDHE2048,
tls.FakeCurveFFDHE3072,
tls.FakeCurveFFDHE4096,
tls.FakeCurveFFDHE6144,
tls.FakeCurveFFDHE8192,
},
},
&tls.ALPNExtension{
AlpnProtocols: []string{
"h3",
},
},
&tls.StatusRequestExtension{},
&tls.FakeDelegatedCredentialsExtension{
SupportedSignatureAlgorithms: []tls.SignatureScheme{
tls.ECDSAWithP256AndSHA256,
tls.ECDSAWithP384AndSHA384,
tls.ECDSAWithP521AndSHA512,
tls.ECDSAWithSHA1,
},
},
&tls.KeyShareExtension{
KeyShares: []tls.KeyShare{
{
Group: tls.X25519,
},
},
},
&tls.SupportedVersionsExtension{
Versions: []uint16{
tls.VersionTLS13,
},
},
&tls.SignatureAlgorithmsExtension{
SupportedSignatureAlgorithms: []tls.SignatureScheme{
tls.ECDSAWithP256AndSHA256,
tls.ECDSAWithP384AndSHA384,
tls.ECDSAWithP521AndSHA512,
tls.ECDSAWithSHA1,
tls.PSSWithSHA256,
tls.PSSWithSHA384,
tls.PSSWithSHA512,
tls.PKCS1WithSHA256,
tls.PKCS1WithSHA384,
tls.PKCS1WithSHA512,
tls.PKCS1WithSHA1,
},
},
&tls.PSKKeyExchangeModesExtension{
Modes: []uint8{
tls.PskModeDHE,
},
},
&tls.FakeRecordSizeLimitExtension{
Limit: 0x4001,
},
ShuffleQUICTransportParameters(&tls.QUICTransportParametersExtension{
TransportParameters: tls.TransportParameters{
tls.InitialMaxStreamDataBidiRemote(0x100000),
tls.InitialMaxStreamsBidi(16),
tls.MaxDatagramFrameSize(1200),
tls.MaxIdleTimeout(30000),
tls.ActiveConnectionIDLimit(8),
&tls.GREASEQUICBit{},
&tls.VersionInformation{
ChoosenVersion: tls.VERSION_1,
AvailableVersions: []uint32{
tls.VERSION_GREASE,
tls.VERSION_1,
},
LegacyID: true,
},
tls.InitialMaxStreamsUni(16),
&tls.GREASETransportParameter{
Length: 2,
},
tls.InitialMaxStreamDataBidiLocal(0xc00000),
tls.InitialMaxStreamDataUni(0x100000),
tls.InitialSourceConnectionID([]byte{}),
tls.MaxAckDelay(20),
tls.InitialMaxData(0x1800000),
&tls.DisableActiveMigration{},
},
}),
&tls.UtlsPaddingExtension{
GetPaddingLen: tls.BoringPaddingStyle,
},
},
},
UDPDatagramMinSize: 1357,
}, nil
default:
return QUICSpec{}, fmt.Errorf("unknown QUIC ID: %v", id)
}
}
func ShuffleTLSExtensions(exts []tls.TLSExtension) []tls.TLSExtension {
mrand.Shuffle(len(exts), func(i, j int) {
exts[i], exts[j] = exts[j], exts[i]
})
return exts
}
func ShuffleQUICTransportParameters(qtp *tls.QUICTransportParametersExtension) *tls.QUICTransportParametersExtension {
// shuffle the order of parameters
mrand.Shuffle(len(qtp.TransportParameters), func(i, j int) {
qtp.TransportParameters[i], qtp.TransportParameters[j] = qtp.TransportParameters[j], qtp.TransportParameters[i]
})
return qtp
}
func VariableLengthGREASEQTP(maxLen int) *tls.GREASETransportParameter {
// get random length for GREASE
greaseMaxLen := big.NewInt(0x10)
greaseLen, err := rand.Int(rand.Reader, greaseMaxLen)
if err != nil {
panic(err)
}
return &tls.GREASETransportParameter{
Length: uint16(greaseLen.Uint64()),
}
}