diff --git a/common.go b/common.go index 7cfc1b16..822ae449 100644 --- a/common.go +++ b/common.go @@ -85,6 +85,7 @@ const ( extensionSignatureAlgorithms uint16 = 13 extensionALPN uint16 = 16 extensionSCT uint16 = 18 + extensionDelegatedCredentials uint16 = 34 extensionSessionTicket uint16 = 35 extensionPreSharedKey uint16 = 41 extensionEarlyData uint16 = 42 @@ -95,6 +96,7 @@ const ( extensionSignatureAlgorithmsCert uint16 = 50 extensionKeyShare uint16 = 51 extensionNextProtoNeg uint16 = 13172 // not IANA assigned + extensionALPS uint16 = 17513 extensionRenegotiationInfo uint16 = 0xff01 ) diff --git a/u_common.go b/u_common.go index 668a677b..febc4d12 100644 --- a/u_common.go +++ b/u_common.go @@ -146,22 +146,32 @@ var ( HelloRandomizedNoALPN = ClientHelloID{helloRandomizedNoALPN, helloAutoVers, nil} // The rest will will parrot given browser. - HelloFirefox_Auto = HelloFirefox_65 + HelloFirefox_Auto = HelloFirefox_102 HelloFirefox_55 = ClientHelloID{helloFirefox, "55", nil} HelloFirefox_56 = ClientHelloID{helloFirefox, "56", nil} HelloFirefox_63 = ClientHelloID{helloFirefox, "63", nil} HelloFirefox_65 = ClientHelloID{helloFirefox, "65", nil} + HelloFirefox_99 = ClientHelloID{helloFirefox, "99", nil} + HelloFirefox_102 = ClientHelloID{helloFirefox, "102", nil} - HelloChrome_Auto = HelloChrome_83 + HelloChrome_Auto = HelloChrome_102 HelloChrome_58 = ClientHelloID{helloChrome, "58", nil} HelloChrome_62 = ClientHelloID{helloChrome, "62", nil} HelloChrome_70 = ClientHelloID{helloChrome, "70", nil} HelloChrome_72 = ClientHelloID{helloChrome, "72", nil} HelloChrome_83 = ClientHelloID{helloChrome, "83", nil} + HelloChrome_87 = ClientHelloID{helloChrome, "87", nil} + HelloChrome_96 = ClientHelloID{helloChrome, "96", nil} + HelloChrome_100 = ClientHelloID{helloChrome, "100", nil} + HelloChrome_102 = ClientHelloID{helloChrome, "102", nil} - HelloIOS_Auto = HelloIOS_12_1 + HelloIOS_Auto = HelloIOS_14 HelloIOS_11_1 = ClientHelloID{helloIOS, "111", nil} // legacy "111" means 11.1 HelloIOS_12_1 = ClientHelloID{helloIOS, "12.1", nil} + HelloIOS_13 = ClientHelloID{helloIOS, "13", nil} + HelloIOS_14 = ClientHelloID{helloIOS, "14", nil} + + HelloAndroid_11_OkHttp = ClientHelloID{helloAndroid, "11", nil} ) // based on spec's GreaseStyle, GREASE_PLACEHOLDER may be replaced by another GREASE value diff --git a/u_parrots.go b/u_parrots.go index eeda357b..43568638 100644 --- a/u_parrots.go +++ b/u_parrots.go @@ -284,6 +284,235 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}, }, }, nil + case HelloChrome_87: + 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: []TLSExtension{ + &UtlsGREASEExtension{}, + &SNIExtension{}, + &UtlsExtendedMasterSecretExtension{}, + &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, + &SupportedCurvesExtension{[]CurveID{ + CurveID(GREASE_PLACEHOLDER), + 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: X25519}, + }}, + &PSKKeyExchangeModesExtension{[]uint8{ + PskModeDHE, + }}, + &SupportedVersionsExtension{[]uint16{ + GREASE_PLACEHOLDER, + VersionTLS13, + VersionTLS12, + VersionTLS11, + VersionTLS10, + }}, + &UtlsCompressCertExtension{[]CertCompressionAlgo{ + CertCompressionBrotli, + }}, + &UtlsGREASEExtension{}, + &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}, + }, + }, nil + case HelloChrome_96: + 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: []TLSExtension{ + &UtlsGREASEExtension{}, + &SNIExtension{}, + &UtlsExtendedMasterSecretExtension{}, + &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, + &SupportedCurvesExtension{[]CurveID{ + CurveID(GREASE_PLACEHOLDER), + 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: X25519}, + }}, + &PSKKeyExchangeModesExtension{[]uint8{ + PskModeDHE, + }}, + &SupportedVersionsExtension{[]uint16{ + GREASE_PLACEHOLDER, + VersionTLS13, + VersionTLS12, + VersionTLS11, + VersionTLS10, + }}, + &UtlsCompressCertExtension{[]CertCompressionAlgo{ + CertCompressionBrotli, + }}, + &ApplicationSettingsExtension{SupportedProtocols: []string{"h2"}}, + &UtlsGREASEExtension{}, + &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}, + }, + }, nil + case HelloChrome_100: + signatureScheme := []SignatureScheme{ + ECDSAWithP256AndSHA256, + ECDSAWithP384AndSHA384, + ECDSAWithP521AndSHA512, + PSSWithSHA256, + PSSWithSHA384, + PSSWithSHA512, + 0x0809, + 0x080a, + 0x080b, + PKCS1WithSHA256, + PKCS1WithSHA384, + PKCS1WithSHA512, + 0x0402, + 0x0303, + 0x0301, + 0x0302, + 0x0203, + 0x0201, + 0x0202, + } + 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: []TLSExtension{ + &UtlsGREASEExtension{}, + &SNIExtension{}, + &UtlsExtendedMasterSecretExtension{}, + &RenegotiationInfoExtension{}, + &SupportedCurvesExtension{[]CurveID{ + GREASE_PLACEHOLDER, + X25519, + CurveP256, + CurveP384, + }}, + &SupportedPointsExtension{SupportedPoints: []byte{ + 0x00, // pointFormatUncompressed + }}, + &SessionTicketExtension{}, + &ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}}, + &StatusRequestExtension{}, + &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: signatureScheme}, + &SCTExtension{}, + &KeyShareExtension{[]KeyShare{ + {Group: CurveID(GREASE_PLACEHOLDER), Data: []byte{0}}, + {Group: X25519}, + }}, + &PSKKeyExchangeModesExtension{[]uint8{ + PskModeDHE, + }}, + &SupportedVersionsExtension{[]uint16{ + VersionTLS13, + VersionTLS12, + VersionTLS11, + VersionTLS10, + }}, + &UtlsCompressCertExtension{[]CertCompressionAlgo{ + CertCompressionBrotli, + }}, + &UtlsGREASEExtension{}, + &ApplicationSettingsExtension{SupportedProtocols: []string{"h2"}}, + &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}, + }, + }, nil case HelloFirefox_55, HelloFirefox_56: return ClientHelloSpec{ TLSVersMax: VersionTLS12, @@ -313,9 +542,232 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { &SupportedCurvesExtension{[]CurveID{X25519, CurveP256, CurveP384, CurveP521}}, &SupportedPointsExtension{SupportedPoints: []byte{pointFormatUncompressed}}, &SessionTicketExtension{}, - &ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}}, + &ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}}, + &StatusRequestExtension{}, + &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{ + ECDSAWithP256AndSHA256, + ECDSAWithP384AndSHA384, + ECDSAWithP521AndSHA512, + PSSWithSHA256, + PSSWithSHA384, + PSSWithSHA512, + PKCS1WithSHA256, + PKCS1WithSHA384, + PKCS1WithSHA512, + ECDSAWithSHA1, + PKCS1WithSHA1}, + }, + &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}, + }, + GetSessionID: nil, + }, nil + case HelloFirefox_63, HelloFirefox_65: + return ClientHelloSpec{ + TLSVersMin: VersionTLS10, + TLSVersMax: VersionTLS13, + CipherSuites: []uint16{ + TLS_AES_128_GCM_SHA256, + TLS_CHACHA20_POLY1305_SHA256, + TLS_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + 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, + FAKE_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + FAKE_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + }, + CompressionMethods: []byte{ + compressionNone, + }, + Extensions: []TLSExtension{ + &SNIExtension{}, + &UtlsExtendedMasterSecretExtension{}, + &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, + &SupportedCurvesExtension{[]CurveID{ + X25519, + CurveP256, + CurveP384, + CurveP521, + CurveID(FakeFFDHE2048), + CurveID(FakeFFDHE3072), + }}, + &SupportedPointsExtension{SupportedPoints: []byte{ + pointFormatUncompressed, + }}, + &SessionTicketExtension{}, + &ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}}, + &StatusRequestExtension{}, + &KeyShareExtension{[]KeyShare{ + {Group: X25519}, + {Group: CurveP256}, + }}, + &SupportedVersionsExtension{[]uint16{ + VersionTLS13, + VersionTLS12, + VersionTLS11, + VersionTLS10}}, + &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{ + ECDSAWithP256AndSHA256, + ECDSAWithP384AndSHA384, + ECDSAWithP521AndSHA512, + PSSWithSHA256, + PSSWithSHA384, + PSSWithSHA512, + PKCS1WithSHA256, + PKCS1WithSHA384, + PKCS1WithSHA512, + ECDSAWithSHA1, + PKCS1WithSHA1, + }}, + &PSKKeyExchangeModesExtension{[]uint8{pskModeDHE}}, + &FakeRecordSizeLimitExtension{0x4001}, + &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}, + }}, nil + case HelloChrome_102: + 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: []TLSExtension{ + &UtlsGREASEExtension{}, + &SNIExtension{}, + &UtlsExtendedMasterSecretExtension{}, + &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, + &SupportedCurvesExtension{[]CurveID{ + GREASE_PLACEHOLDER, + 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: X25519}, + }}, + &PSKKeyExchangeModesExtension{[]uint8{ + PskModeDHE, + }}, + &SupportedVersionsExtension{[]uint16{ + GREASE_PLACEHOLDER, + VersionTLS13, + VersionTLS12, + }}, + &UtlsCompressCertExtension{[]CertCompressionAlgo{ + CertCompressionBrotli, + }}, + &ApplicationSettingsExtension{SupportedProtocols: []string{"h2"}}, + &UtlsGREASEExtension{}, + &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}, + }, + }, nil + + case HelloFirefox_99: + return ClientHelloSpec{ + TLSVersMin: VersionTLS10, + TLSVersMax: VersionTLS13, + CipherSuites: []uint16{ + TLS_AES_128_GCM_SHA256, + TLS_CHACHA20_POLY1305_SHA256, + TLS_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + 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, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + }, + CompressionMethods: []byte{ + compressionNone, + }, + Extensions: []TLSExtension{ + &SNIExtension{}, //server_name + &UtlsExtendedMasterSecretExtension{}, //extended_master_secret + &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, //extensionRenegotiationInfo + &SupportedCurvesExtension{[]CurveID{ //supported_groups + X25519, + CurveP256, + CurveP384, + CurveP521, + CurveID(FakeFFDHE2048), + CurveID(FakeFFDHE3072), + }}, + &SupportedPointsExtension{SupportedPoints: []byte{ //ec_point_formats + pointFormatUncompressed, + }}, + &SessionTicketExtension{}, + &ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}}, //application_layer_protocol_negotiation &StatusRequestExtension{}, - &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{ + &DelegatedCredentialsExtension{ + AlgorithmsSignature: []SignatureScheme{ //signature_algorithms + ECDSAWithP256AndSHA256, + ECDSAWithP384AndSHA384, + ECDSAWithP521AndSHA512, + ECDSAWithSHA1, + }, + }, + &KeyShareExtension{[]KeyShare{ + {Group: X25519}, + {Group: CurveP256}, //key_share + }}, + &SupportedVersionsExtension{[]uint16{ + VersionTLS13, //supported_versions + VersionTLS12, + VersionTLS11, + VersionTLS10, + }}, + &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{ //signature_algorithms ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512, @@ -326,13 +778,15 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { PKCS1WithSHA384, PKCS1WithSHA512, ECDSAWithSHA1, - PKCS1WithSHA1}, - }, - &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}, - }, - GetSessionID: nil, - }, nil - case HelloFirefox_63, HelloFirefox_65: + PKCS1WithSHA1, + }}, + &PSKKeyExchangeModesExtension{[]uint8{ //psk_key_exchange_modes + PskModeDHE, + }}, + &FakeRecordSizeLimitExtension{Limit: 0x4001}, //record_size_limit + &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}, //padding + }}, nil + case HelloFirefox_102: return ClientHelloSpec{ TLSVersMin: VersionTLS10, TLSVersMax: VersionTLS13, @@ -350,20 +804,19 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - FAKE_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - FAKE_TLS_DHE_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, - TLS_RSA_WITH_3DES_EDE_CBC_SHA, }, CompressionMethods: []byte{ compressionNone, }, Extensions: []TLSExtension{ - &SNIExtension{}, - &UtlsExtendedMasterSecretExtension{}, - &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, - &SupportedCurvesExtension{[]CurveID{ + &SNIExtension{}, //server_name + &UtlsExtendedMasterSecretExtension{}, //extended_master_secret + &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, //extensionRenegotiationInfo + &SupportedCurvesExtension{[]CurveID{ //supported_groups X25519, CurveP256, CurveP384, @@ -371,22 +824,29 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { CurveID(FakeFFDHE2048), CurveID(FakeFFDHE3072), }}, - &SupportedPointsExtension{SupportedPoints: []byte{ + &SupportedPointsExtension{SupportedPoints: []byte{ //ec_point_formats pointFormatUncompressed, }}, &SessionTicketExtension{}, - &ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}}, + &ALPNExtension{AlpnProtocols: []string{"h2"}}, //application_layer_protocol_negotiation &StatusRequestExtension{}, + &DelegatedCredentialsExtension{ + AlgorithmsSignature: []SignatureScheme{ //signature_algorithms + ECDSAWithP256AndSHA256, + ECDSAWithP384AndSHA384, + ECDSAWithP521AndSHA512, + ECDSAWithSHA1, + }, + }, &KeyShareExtension{[]KeyShare{ {Group: X25519}, - {Group: CurveP256}, + {Group: CurveP256}, //key_share }}, &SupportedVersionsExtension{[]uint16{ - VersionTLS13, + VersionTLS13, //supported_versions VersionTLS12, - VersionTLS11, - VersionTLS10}}, - &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{ + }}, + &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{ //signature_algorithms ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512, @@ -399,9 +859,11 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { ECDSAWithSHA1, PKCS1WithSHA1, }}, - &PSKKeyExchangeModesExtension{[]uint8{pskModeDHE}}, - &FakeRecordSizeLimitExtension{0x4001}, - &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}, + &PSKKeyExchangeModesExtension{[]uint8{ //psk_key_exchange_modes + PskModeDHE, + }}, + &FakeRecordSizeLimitExtension{Limit: 0x4001}, //record_size_limit + &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}, //padding }}, nil case HelloIOS_11_1: return ClientHelloSpec{ @@ -524,6 +986,214 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { }}, }, }, nil + case HelloIOS_13: + return ClientHelloSpec{ + CipherSuites: []uint16{ + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384, + TLS_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + DISABLED_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + DISABLED_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_RSA_WITH_AES_128_GCM_SHA256, + DISABLED_TLS_RSA_WITH_AES_256_CBC_SHA256, + TLS_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + 0xc008, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + }, + CompressionMethods: []byte{ + 0x00, // compressionNone + }, + Extensions: []TLSExtension{ + &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, + &SNIExtension{}, + &UtlsExtendedMasterSecretExtension{}, + &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{ + ECDSAWithP256AndSHA256, + PSSWithSHA256, + PKCS1WithSHA256, + ECDSAWithP384AndSHA384, + ECDSAWithSHA1, + PSSWithSHA384, + PSSWithSHA384, + PKCS1WithSHA384, + PSSWithSHA512, + PKCS1WithSHA512, + PKCS1WithSHA1, + }}, + &StatusRequestExtension{}, + &SCTExtension{}, + &ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}}, + &SupportedPointsExtension{SupportedPoints: []byte{ + 0x00, // pointFormatUncompressed + }}, + &KeyShareExtension{[]KeyShare{ + {Group: X25519}, + }}, + &PSKKeyExchangeModesExtension{[]uint8{ + PskModeDHE, + }}, + &SupportedVersionsExtension{[]uint16{ + VersionTLS13, + VersionTLS12, + VersionTLS11, + VersionTLS10, + }}, + &SupportedCurvesExtension{[]CurveID{ + X25519, + CurveP256, + CurveP384, + CurveP521, + }}, + &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}, + }, + }, nil + case HelloIOS_14: + return ClientHelloSpec{ + // TLSVersMax: VersionTLS12, + // TLSVersMin: VersionTLS10, + CipherSuites: []uint16{ + GREASE_PLACEHOLDER, + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384, + TLS_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + DISABLED_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + DISABLED_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_RSA_WITH_AES_128_GCM_SHA256, + DISABLED_TLS_RSA_WITH_AES_256_CBC_SHA256, + TLS_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + 0xc008, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + }, + CompressionMethods: []byte{ + 0x00, // compressionNone + }, + Extensions: []TLSExtension{ + &UtlsGREASEExtension{}, + &SNIExtension{}, + &UtlsExtendedMasterSecretExtension{}, + &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, + &SupportedCurvesExtension{[]CurveID{ + CurveID(GREASE_PLACEHOLDER), + X25519, + CurveP256, + CurveP384, + CurveP521, + }}, + &SupportedPointsExtension{SupportedPoints: []byte{ + 0x00, // pointFormatUncompressed + }}, + &ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}}, + &StatusRequestExtension{}, + &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{ + ECDSAWithP256AndSHA256, + PSSWithSHA256, + PKCS1WithSHA256, + ECDSAWithP384AndSHA384, + ECDSAWithSHA1, + PSSWithSHA384, + PSSWithSHA384, + PKCS1WithSHA384, + PSSWithSHA512, + PKCS1WithSHA512, + PKCS1WithSHA1, + }}, + &SCTExtension{}, + &KeyShareExtension{[]KeyShare{ + {Group: CurveID(GREASE_PLACEHOLDER), Data: []byte{0}}, + {Group: X25519}, + }}, + &PSKKeyExchangeModesExtension{[]uint8{ + PskModeDHE, + }}, + &SupportedVersionsExtension{[]uint16{ + GREASE_PLACEHOLDER, + VersionTLS13, + VersionTLS12, + VersionTLS11, + VersionTLS10, + }}, + &UtlsGREASEExtension{}, + &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}, + }, + }, nil + case HelloAndroid_11_OkHttp: + return ClientHelloSpec{ + 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_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_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: []TLSExtension{ + &SNIExtension{}, + &UtlsExtendedMasterSecretExtension{}, + &RenegotiationInfoExtension{}, + // supported_groups + &SupportedCurvesExtension{[]CurveID{ + X25519, + CurveP256, + CurveP384, + }}, + &SupportedPointsExtension{SupportedPoints: []byte{ + 0x00, // pointFormatUncompressed + }}, + &StatusRequestExtension{}, + &SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{ + ECDSAWithP256AndSHA256, + PSSWithSHA256, + PKCS1WithSHA256, + ECDSAWithP384AndSHA384, + PSSWithSHA384, + PKCS1WithSHA384, + PSSWithSHA512, + PKCS1WithSHA512, + PKCS1WithSHA1, + }}, + }, + }, nil default: return ClientHelloSpec{}, errors.New("ClientHello ID " + id.Str() + " is unknown") } @@ -858,6 +1528,32 @@ func (uconn *UConn) generateRandomizedSpec() (ClientHelloSpec, error) { Versions: makeSupportedVersions(p.TLSVersMin, p.TLSVersMax), } p.Extensions = append(p.Extensions, &ks, &pskExchangeModes, &supportedVersionsExt) + + // Randomly add an ALPS extension. ALPS is TLS 1.3-only and may only + // appear when an ALPN extension is present + // (https://datatracker.ietf.org/doc/html/draft-vvv-tls-alps-01#section-3). + // ALPS is a draft specification at this time, but appears in + // Chrome/BoringSSL. + if WithALPN { + + // ALPS is a new addition to generateRandomizedSpec. Use a salted + // seed to create a new, independent PRNG, so that a seed used + // with the previous version of generateRandomizedSpec will + // produce the exact same spec as long as ALPS isn't selected. + r, err := newPRNGWithSaltedSeed(uconn.ClientHelloID.Seed, "ALPS") + if err != nil { + return p, err + } + if r.FlipWeightedCoin(0.33) { + // As with the ALPN case above, default to something popular + // (unlike ALPN, ALPS can't yet be specified in uconn.config). + alps := &ApplicationSettingsExtension{SupportedProtocols: []string{"h2"}} + p.Extensions = append(p.Extensions, alps) + } + } + + // TODO: randomly add DelegatedCredentialsExtension, once it is + // sufficiently popular. } r.rand.Shuffle(len(p.Extensions), func(i, j int) { p.Extensions[i], p.Extensions[j] = p.Extensions[j], p.Extensions[i] diff --git a/u_prng.go b/u_prng.go index 9a30cd7f..e00ab6f0 100644 --- a/u_prng.go +++ b/u_prng.go @@ -19,6 +19,7 @@ import ( "math/rand" "sync" + "golang.org/x/crypto/hkdf" "golang.org/x/crypto/sha3" ) @@ -39,6 +40,21 @@ func NewPRNGSeed() (*PRNGSeed, error) { return seed, nil } +// newSaltedPRNGSeed creates a new seed derived from an existing seed and a +// salt. A HKDF is applied to the seed and salt. +// +// newSaltedPRNGSeed is intended for use cases where a single seed needs to be +// used in distinct contexts to produce independent random streams. +func newSaltedPRNGSeed(seed *PRNGSeed, salt string) (*PRNGSeed, error) { + saltedSeed := new(PRNGSeed) + _, err := io.ReadFull( + hkdf.New(sha3.New256, seed[:], []byte(salt), nil), saltedSeed[:]) + if err != nil { + return nil, err + } + return saltedSeed, nil +} + // prng is a seeded, unbiased PRNG based on SHAKE256. that is suitable for use // cases such as obfuscation. Seeding is based on crypto/rand.Read. // @@ -78,6 +94,16 @@ func newPRNGWithSeed(seed *PRNGSeed) (*prng, error) { return p, nil } +// newPRNGWithSaltedSeed initializes a new PRNG using a seed derived from an +// existing seed and a salt with NewSaltedSeed. +func newPRNGWithSaltedSeed(seed *PRNGSeed, salt string) (*prng, error) { + saltedSeed, err := newSaltedPRNGSeed(seed, salt) + if err != nil { + return nil, err + } + return newPRNGWithSeed(saltedSeed) +} + // Read reads random bytes from the PRNG stream into b. Read conforms to // io.Reader and always returns len(p), nil. func (p *prng) Read(b []byte) (int, error) { diff --git a/u_tls_extensions.go b/u_tls_extensions.go index 8bf4f4b0..3e878753 100644 --- a/u_tls_extensions.go +++ b/u_tls_extensions.go @@ -79,7 +79,7 @@ func (e *SNIExtension) Read(b []byte) (int, error) { b[0] = byte(extensionServerName >> 8) b[1] = byte(extensionServerName) b[2] = byte((len(hostName) + 5) >> 8) - b[3] = byte((len(hostName) + 5)) + b[3] = byte(len(hostName) + 5) b[4] = byte((len(hostName) + 3) >> 8) b[5] = byte(len(hostName) + 3) // b[6] Server Name Type: host_name (0) @@ -115,6 +115,36 @@ func (e *StatusRequestExtension) Read(b []byte) (int, error) { return e.Len(), io.EOF } +type StatusRequestV2Extension struct { +} + +func (e *StatusRequestV2Extension) writeToUConn(uc *UConn) error { + uc.HandshakeState.Hello.OcspStapling = true + return nil +} + +func (e *StatusRequestV2Extension) Len() int { + return 13 +} + +func (e *StatusRequestV2Extension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + // RFC 4366, section 3.6 + b[0] = byte(17 >> 8) + b[1] = byte(17) + b[2] = 0 + b[3] = 9 + b[4] = 0 + b[5] = 7 + b[6] = 2 // OCSP type + b[7] = 0 + b[8] = 4 + // Two zero valued uint16s for the two lengths. + return e.Len(), io.EOF +} + type SupportedCurvesExtension struct { Curves []CurveID } @@ -137,9 +167,9 @@ func (e *SupportedCurvesExtension) Read(b []byte) (int, error) { b[0] = byte(extensionSupportedCurves >> 8) b[1] = byte(extensionSupportedCurves) b[2] = byte((2 + 2*len(e.Curves)) >> 8) - b[3] = byte((2 + 2*len(e.Curves))) + b[3] = byte(2 + 2*len(e.Curves)) b[4] = byte((2 * len(e.Curves)) >> 8) - b[5] = byte((2 * len(e.Curves))) + b[5] = byte(2 * len(e.Curves)) for i, curve := range e.Curves { b[6+2*i] = byte(curve >> 8) b[7+2*i] = byte(curve) @@ -168,8 +198,8 @@ func (e *SupportedPointsExtension) Read(b []byte) (int, error) { b[0] = byte(extensionSupportedPoints >> 8) b[1] = byte(extensionSupportedPoints) b[2] = byte((1 + len(e.SupportedPoints)) >> 8) - b[3] = byte((1 + len(e.SupportedPoints))) - b[4] = byte((len(e.SupportedPoints))) + b[3] = byte(1 + len(e.SupportedPoints)) + b[4] = byte(len(e.SupportedPoints)) for i, pointFormat := range e.SupportedPoints { b[5+i] = pointFormat } @@ -197,9 +227,40 @@ func (e *SignatureAlgorithmsExtension) Read(b []byte) (int, error) { b[0] = byte(extensionSignatureAlgorithms >> 8) b[1] = byte(extensionSignatureAlgorithms) b[2] = byte((2 + 2*len(e.SupportedSignatureAlgorithms)) >> 8) - b[3] = byte((2 + 2*len(e.SupportedSignatureAlgorithms))) + b[3] = byte(2 + 2*len(e.SupportedSignatureAlgorithms)) + b[4] = byte((2 * len(e.SupportedSignatureAlgorithms)) >> 8) + b[5] = byte(2 * len(e.SupportedSignatureAlgorithms)) + for i, sigAndHash := range e.SupportedSignatureAlgorithms { + b[6+2*i] = byte(sigAndHash >> 8) + b[7+2*i] = byte(sigAndHash) + } + return e.Len(), io.EOF +} + +type SignatureAlgorithmsCertExtension struct { + SupportedSignatureAlgorithms []SignatureScheme +} + +func (e *SignatureAlgorithmsCertExtension) writeToUConn(uc *UConn) error { + uc.HandshakeState.Hello.SupportedSignatureAlgorithms = e.SupportedSignatureAlgorithms + return nil +} + +func (e *SignatureAlgorithmsCertExtension) Len() int { + return 6 + 2*len(e.SupportedSignatureAlgorithms) +} + +func (e *SignatureAlgorithmsCertExtension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 + b[0] = byte(extensionSignatureAlgorithmsCert >> 8) + b[1] = byte(extensionSignatureAlgorithmsCert) + b[2] = byte((2 + 2*len(e.SupportedSignatureAlgorithms)) >> 8) + b[3] = byte(2 + 2*len(e.SupportedSignatureAlgorithms)) b[4] = byte((2 * len(e.SupportedSignatureAlgorithms)) >> 8) - b[5] = byte((2 * len(e.SupportedSignatureAlgorithms))) + b[5] = byte(2 * len(e.SupportedSignatureAlgorithms)) for i, sigAndHash := range e.SupportedSignatureAlgorithms { b[6+2*i] = byte(sigAndHash >> 8) b[7+2*i] = byte(sigAndHash) @@ -295,6 +356,52 @@ func (e *ALPNExtension) Read(b []byte) (int, error) { return e.Len(), io.EOF } +type ApplicationSettingsExtension struct { + SupportedProtocols []string +} + +func (e *ApplicationSettingsExtension) writeToUConn(uc *UConn) error { + return nil +} + +func (e *ApplicationSettingsExtension) Len() int { + bLen := 2 + 2 + 2 // Type + Length + ALPS Extension length + for _, s := range e.SupportedProtocols { + bLen += 1 + len(s) // Supported ALPN Length + actual length of protocol + } + return bLen +} + +func (e *ApplicationSettingsExtension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + + // Read Type. + b[0] = byte(extensionALPS >> 8) // hex: 44 dec: 68 + b[1] = byte(extensionALPS & 0xff) // hex: 69 dec: 105 + + lengths := b[2:] // get the remaining buffer without Type + b = b[6:] // set the buffer to the buffer without Type, Length and ALPS Extension Length (so only the Supported ALPN list remains) + + stringsLength := 0 + for _, s := range e.SupportedProtocols { + l := len(s) // Supported ALPN Length + b[0] = byte(l) // Supported ALPN Length in bytes hex: 02 dec: 2 + copy(b[1:], s) // copy the Supported ALPN as bytes to the buffer + b = b[1+l:] // set the buffer to the buffer without the Supported ALPN Length and Supported ALPN (so we can continue to the next protocol in this loop) + stringsLength += 1 + l // Supported ALPN Length (the field itself) + Supported ALPN Length (the value) + } + + lengths[2] = byte(stringsLength >> 8) // ALPS Extension Length hex: 00 dec: 0 + lengths[3] = byte(stringsLength) // ALPS Extension Length hex: 03 dec: 3 + stringsLength += 2 // plus ALPS Extension Length field length + lengths[0] = byte(stringsLength >> 8) // Length hex:00 dec: 0 + lengths[1] = byte(stringsLength) // Length hex: 05 dec: 5 + + return e.Len(), io.EOF +} + type SCTExtension struct { } @@ -598,9 +705,9 @@ func (e *KeyShareExtension) Read(b []byte) (int, error) { b[1] = byte(extensionKeyShare) keySharesLen := e.keySharesLen() b[2] = byte((keySharesLen + 2) >> 8) - b[3] = byte((keySharesLen + 2)) + b[3] = byte(keySharesLen + 2) b[4] = byte((keySharesLen) >> 8) - b[5] = byte((keySharesLen)) + b[5] = byte(keySharesLen) i := 6 for _, ks := range e.KeyShares { @@ -642,7 +749,7 @@ func (e *PSKKeyExchangeModesExtension) Read(b []byte) (int, error) { modesLen := len(e.Modes) b[2] = byte((modesLen + 1) >> 8) - b[3] = byte((modesLen + 1)) + b[3] = byte(modesLen + 1) b[4] = byte(modesLen) if len(e.Modes) > 0 { @@ -682,7 +789,7 @@ func (e *SupportedVersionsExtension) Read(b []byte) (int, error) { b[0] = byte(extensionSupportedVersions >> 8) b[1] = byte(extensionSupportedVersions) b[2] = byte((extLen + 1) >> 8) - b[3] = byte((extLen + 1)) + b[3] = byte(extLen + 1) b[4] = byte(extLen) i := 5 @@ -775,3 +882,32 @@ func (e *FakeRecordSizeLimitExtension) Read(b []byte) (int, error) { b[5] = byte(e.Limit & 0xff) return e.Len(), io.EOF } + +type DelegatedCredentialsExtension struct { + AlgorithmsSignature []SignatureScheme +} + +func (e *DelegatedCredentialsExtension) writeToUConn(uc *UConn) error { + return nil +} + +func (e *DelegatedCredentialsExtension) Len() int { + return 6 + 2*len(e.AlgorithmsSignature) +} + +func (e *DelegatedCredentialsExtension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + b[0] = byte(extensionDelegatedCredentials >> 8) + b[1] = byte(extensionDelegatedCredentials) + b[2] = byte((2 + 2*len(e.AlgorithmsSignature)) >> 8) + b[3] = byte(2 + 2*len(e.AlgorithmsSignature)) + b[4] = byte((2 * len(e.AlgorithmsSignature)) >> 8) + b[5] = byte(2 * len(e.AlgorithmsSignature)) + for i, sigAndHash := range e.AlgorithmsSignature { + b[6+2*i] = byte(sigAndHash >> 8) + b[7+2*i] = byte(sigAndHash) + } + return e.Len(), io.EOF +}