diff --git a/common.go b/common.go index 73b6dad51c..a2d0335811 100644 --- a/common.go +++ b/common.go @@ -130,6 +130,7 @@ const ( extensionRenegotiationInfo uint16 = 0xff01 extensionECHOuterExtensions uint16 = 0xfd00 extensionEncryptedClientHello uint16 = 0xfe0d + extensionTrustAnchors uint16 = 0xca34 ) // TLS signaling cipher suite values @@ -152,6 +153,8 @@ const ( CurveP521 CurveID = 25 X25519 CurveID = 29 X25519MLKEM768 CurveID = 4588 + FFDHE2048 CurveID = 0x0100 + FFDHE3072 CurveID = 0x0101 ) func isTLS13OnlyKeyExchange(curve CurveID) bool { @@ -393,6 +396,7 @@ type ClientSessionCache interface { // SignatureScheme identifies a signature algorithm supported by TLS. See // RFC 8446, Section 4.2.3. +// RFC 9881 for PQ type SignatureScheme uint16 const ( @@ -417,6 +421,11 @@ const ( // Legacy signature and hash algorithms for TLS 1.2. PKCS1WithSHA1 SignatureScheme = 0x0201 ECDSAWithSHA1 SignatureScheme = 0x0203 + + // MLDSA algorithms for PQ cryptography + MLDSA44 SignatureScheme = 0x0904 + MLDSA65 SignatureScheme = 0x0905 + MLDSA87 SignatureScheme = 0x0906 ) // ClientHelloInfo contains information from a ClientHello message in order to diff --git a/handshake_messages.go b/handshake_messages.go index c382b3346a..706d2ea09e 100644 --- a/handshake_messages.go +++ b/handshake_messages.go @@ -98,6 +98,7 @@ type clientHelloMsg struct { pskBinders [][]byte quicTransportParameters []byte encryptedClientHello []byte + trustAnchors bool // extensions are only populated on the server-side of a handshake extensions []uint16 @@ -318,6 +319,17 @@ func (m *clientHelloMsg) marshalMsgReorderOuterExts(echInner bool, outerExts []u }) } } + if m.trustAnchors { + // RFC 9881 + if echInner { + echOuterExts = append(echOuterExts, extensionTrustAnchors) + } else { + exts.AddUint16(extensionTrustAnchors) + exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { + exts.AddUint16(0) // 00 00 + }) + } + } // [uTLS SECTION BEGIN] // reorder OuterExtensions according to their order in the spec if echInner && outerExts != nil { @@ -694,6 +706,12 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { } m.pskBinders = append(m.pskBinders, binder) } + case extensionTrustAnchors: + var data uint16 + if !extData.ReadUint16(&data) { + return false + } + m.trustAnchors = data == 0 case extensionEncryptedClientHello: if !extData.ReadBytes(&m.encryptedClientHello, len(extData)) { return false diff --git a/handshake_messages_test.go b/handshake_messages_test.go index b71d97ca26..8f5aab7598 100644 --- a/handshake_messages_test.go +++ b/handshake_messages_test.go @@ -234,6 +234,9 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value { if rand.Intn(10) > 5 { m.earlyData = true } + if rand.Intn(10) > 5 { + m.trustAnchors = true + } if rand.Intn(10) > 5 { m.encryptedClientHello = randomBytes(rand.Intn(50)+1, rand) } diff --git a/u_common.go b/u_common.go index a9f90f75e3..a771104651 100644 --- a/u_common.go +++ b/u_common.go @@ -645,6 +645,10 @@ var ( HelloChrome_131 = ClientHelloID{helloChrome, "131", nil, nil} // Chrome w/ New ALPS codepoint HelloChrome_133 = ClientHelloID{helloChrome, "133", nil, nil} + // Chrome w/ Trust anchors (Draft) + HelloChrome_141_TA = ClientHelloID{helloChrome, "141", nil, nil} + // Chrome w/ TA and PQ MLDSA Signatures (Experimental feature) + HelloChrome_144_TA_PQS = ClientHelloID{helloChrome, "144", nil, nil} HelloIOS_Auto = HelloIOS_14 HelloIOS_11_1 = ClientHelloID{helloIOS, "111", nil, nil} // legacy "111" means 11.1 @@ -652,11 +656,15 @@ var ( HelloIOS_13 = ClientHelloID{helloIOS, "13", nil, nil} HelloIOS_14 = ClientHelloID{helloIOS, "14", nil, nil} - HelloAndroid_11_OkHttp = ClientHelloID{helloAndroid, "11", nil, nil} + HelloAndroid_OkHttp_Auto = HelloAndroid_16_OkHttp + HelloAndroid_11_OkHttp = ClientHelloID{helloAndroid, "11", nil, nil} + HelloAndroid_16_OkHttp = ClientHelloID{helloAndroid, "16", nil, nil} - HelloEdge_Auto = HelloEdge_85 // HelloEdge_106 seems to be incompatible with this library + // Probably should be deleted completely + HelloEdge_Auto = HelloEdge_133 // HelloEdge_106 seems to be incompatible with this library HelloEdge_85 = ClientHelloID{helloEdge, "85", nil, nil} HelloEdge_106 = ClientHelloID{helloEdge, "106", nil, nil} + HelloEdge_133 = HelloChrome_133 // It's basically Chrome 1:1 HelloSafari_Auto = HelloSafari_26_3 HelloSafari_16_0 = ClientHelloID{helloSafari, "16.0", nil, nil} diff --git a/u_parrots.go b/u_parrots.go index 8416fa3363..89f5a4772b 100644 --- a/u_parrots.go +++ b/u_parrots.go @@ -964,6 +964,157 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { &UtlsGREASEExtension{}, }), }, nil + case HelloChrome_141_TA: + return ClientHelloSpec{ + CipherSuites: []uint16{ + GREASE_PLACEHOLDER, + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384, + TLS_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + }, + CompressionMethods: []byte{ + 0x00, // compressionNone + }, + Extensions: ShuffleChromeTLSExtensions([]TLSExtension{ + &UtlsGREASEExtension{}, + &SNIExtension{}, + &ExtendedMasterSecretExtension{}, + &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, + &SupportedCurvesExtension{[]CurveID{ + GREASE_PLACEHOLDER, + X25519MLKEM768, + X25519, + CurveP256, + CurveP384, + }}, + &SupportedPointsExtension{SupportedPoints: []byte{ + 0x00, // pointFormatUncompressed + }}, + &SessionTicketExtension{}, + &ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}}, + &StatusRequestExtension{}, + &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{ + ECDSAWithP256AndSHA256, + PSSWithSHA256, + PKCS1WithSHA256, + ECDSAWithP384AndSHA384, + PSSWithSHA384, + PKCS1WithSHA384, + PSSWithSHA512, + PKCS1WithSHA512, + }}, + &SCTExtension{}, + &KeyShareExtension{[]KeyShare{ + {Group: CurveID(GREASE_PLACEHOLDER), Data: []byte{0}}, + {Group: X25519MLKEM768}, + {Group: X25519}, + }}, + &PSKKeyExchangeModesExtension{[]uint8{ + PskModeDHE, + }}, + &SupportedVersionsExtension{[]uint16{ + GREASE_PLACEHOLDER, + VersionTLS13, + VersionTLS12, + }}, + &UtlsCompressCertExtension{[]CertCompressionAlgo{ + CertCompressionBrotli, + }}, + &ApplicationSettingsExtensionNew{SupportedProtocols: []string{"h2"}}, + &TrustAnchorsExtension{}, + BoringGREASEECH(), + &UtlsGREASEExtension{}, + }), + }, nil + case HelloChrome_144_TA_PQS: + return ClientHelloSpec{ + CipherSuites: []uint16{ + GREASE_PLACEHOLDER, + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384, + TLS_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + }, + CompressionMethods: []byte{ + 0x00, // compressionNone + }, + Extensions: ShuffleChromeTLSExtensions([]TLSExtension{ + &UtlsGREASEExtension{}, + &SNIExtension{}, + &ExtendedMasterSecretExtension{}, + &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, + &SupportedCurvesExtension{[]CurveID{ + GREASE_PLACEHOLDER, + X25519MLKEM768, + X25519, + CurveP256, + CurveP384, + }}, + &SupportedPointsExtension{SupportedPoints: []byte{ + 0x00, // pointFormatUncompressed + }}, + &SessionTicketExtension{}, + &ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}}, + &StatusRequestExtension{}, + &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{ + MLDSA44, + MLDSA65, + MLDSA87, + ECDSAWithP256AndSHA256, + PSSWithSHA256, + PKCS1WithSHA256, + ECDSAWithP384AndSHA384, + PSSWithSHA384, + PKCS1WithSHA384, + PSSWithSHA512, + PKCS1WithSHA512, + }}, + &SCTExtension{}, + &KeyShareExtension{[]KeyShare{ + {Group: CurveID(GREASE_PLACEHOLDER), Data: []byte{0}}, + {Group: X25519MLKEM768}, + {Group: X25519}, + }}, + &PSKKeyExchangeModesExtension{[]uint8{ + PskModeDHE, + }}, + &SupportedVersionsExtension{[]uint16{ + GREASE_PLACEHOLDER, + VersionTLS13, + VersionTLS12, + }}, + &UtlsCompressCertExtension{[]CertCompressionAlgo{ + CertCompressionBrotli, + }}, + &ApplicationSettingsExtensionNew{SupportedProtocols: []string{"h2"}}, + &TrustAnchorsExtension{}, + BoringGREASEECH(), + &UtlsGREASEExtension{}, + }), + }, nil case HelloFirefox_55, HelloFirefox_56: return ClientHelloSpec{ TLSVersMax: VersionTLS12, @@ -1481,7 +1632,6 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256, @@ -1505,8 +1655,8 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { CurveP256, CurveP384, CurveP521, - 0x0100, - 0x0101, + FFDHE2048, + FFDHE3072, }, }, &SupportedPointsExtension{ @@ -1514,6 +1664,7 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { 0x0, // uncompressed }, }, + &SessionTicketExtension{}, // Actually it's supported from Firefox 3.0 &ALPNExtension{ AlpnProtocols: []string{ "h2", @@ -1566,6 +1717,9 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { PKCS1WithSHA1, }, }, + &PSKKeyExchangeModesExtension{[]uint8{ + PskModeDHE, + }}, // This is default since Firefox 60 &FakeRecordSizeLimitExtension{ Limit: 0x4001, }, @@ -1877,10 +2031,10 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { CipherSuites: []uint16{ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - 0xcca9, // Cipher Suite: TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca9) + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - 0xcca8, // Cipher Suite: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8) + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256, @@ -1918,6 +2072,67 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { }}, }, }, nil + case HelloAndroid_16_OkHttp: + return ClientHelloSpec{ + CipherSuites: []uint16{ + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384, + TLS_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + }, + CompressionMethods: []byte{ + 0x00, + }, + Extensions: []TLSExtension{ + &SNIExtension{}, + &ExtendedMasterSecretExtension{}, + &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, + &SupportedCurvesExtension{Curves: []CurveID{ + X25519, + CurveP256, + CurveP384, + }}, + &SupportedPointsExtension{SupportedPoints: []byte{ + 0x00, + }}, + &SessionTicketExtension{}, + &ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}}, + &StatusRequestExtension{}, + &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{ + ECDSAWithP256AndSHA256, + PSSWithSHA256, + PKCS1WithSHA256, + ECDSAWithP384AndSHA384, + PSSWithSHA384, + PKCS1WithSHA384, + PSSWithSHA512, + PKCS1WithSHA512, + PKCS1WithSHA1, + }}, + &KeyShareExtension{KeyShares: []KeyShare{ + {Group: X25519}, + }}, + &PSKKeyExchangeModesExtension{Modes: []uint8{ + PskModeDHE, + }}, + &SupportedVersionsExtension{Versions: []uint16{ + VersionTLS13, + VersionTLS12, + }}, + &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}, + }, + }, nil case HelloEdge_85: return ClientHelloSpec{ CipherSuites: []uint16{ diff --git a/u_public.go b/u_public.go index caaf693d0f..160f372947 100644 --- a/u_public.go +++ b/u_public.go @@ -399,6 +399,7 @@ type PubClientHelloMsg struct { PskIdentities []PskIdentity PskBinders [][]byte QuicTransportParameters []byte + TrustAnchors bool cachedPrivateHello *clientHelloMsg // todo: further optimize to reduce clientHelloMsg construction encryptedClientHello []byte @@ -437,6 +438,7 @@ func (chm *PubClientHelloMsg) getPrivatePtr() *clientHelloMsg { pskIdentities: PskIdentities(chm.PskIdentities).ToPrivate(), pskBinders: chm.PskBinders, quicTransportParameters: chm.QuicTransportParameters, + trustAnchors: chm.TrustAnchors, encryptedClientHello: chm.encryptedClientHello, nextProtoNeg: chm.NextProtoNeg, @@ -488,6 +490,7 @@ func (chm *clientHelloMsg) getPublicPtr() *PubClientHelloMsg { PskIdentities: pskIdentities(chm.pskIdentities).ToPublic(), PskBinders: chm.pskBinders, QuicTransportParameters: chm.quicTransportParameters, + TrustAnchors: chm.trustAnchors, cachedPrivateHello: chm, encryptedClientHello: chm.encryptedClientHello, } diff --git a/u_tls_extensions.go b/u_tls_extensions.go index 6b2ef4865d..1a24f11af4 100644 --- a/u_tls_extensions.go +++ b/u_tls_extensions.go @@ -1704,6 +1704,48 @@ func (e *RenegotiationInfoExtension) writeToUConn(uc *UConn) error { return nil } +// TrustAnchorsExtension implements trust_anchors +type TrustAnchorsExtension struct { +} + +func (e *TrustAnchorsExtension) Len() int { + return 6 +} + +func (e *TrustAnchorsExtension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + b[0] = byte(extensionTrustAnchors >> 8) + b[1] = byte(extensionTrustAnchors & 0xff) + b[2] = 0 + b[3] = 2 + // 00 00 + return e.Len(), io.EOF +} + +func (e *TrustAnchorsExtension) UnmarshalJSON(_ []byte) error { + return nil +} + +func (e *TrustAnchorsExtension) Write(b []byte) (int, error) { + fullLen := len(b) + extData := cryptobyte.String(b) + var data uint16 + if !extData.ReadUint16(&data) { + return fullLen, errors.New("unable to read trust anchors extension data") + } + if data != 0 { + return fullLen, errors.New("trust anchors extension data is not empty") // todo: support non empty + } + return fullLen, nil +} + +func (e *TrustAnchorsExtension) writeToUConn(uc *UConn) error { + uc.HandshakeState.Hello.TrustAnchors = true + return nil +} + /* FAKE EXTENSIONS */