mirror of
https://github.com/refraction-networking/utls.git
synced 2025-04-03 20:17:36 +03:00
feat: ClientHello JSON Unmarshaler
Allowing unmarshalling a JSON object into a ClientHelloSpec.
This commit is contained in:
parent
54bb4cd3f7
commit
585eaf84f7
6 changed files with 349 additions and 66 deletions
75
testdata/ClientHello-JSON-Chrome102.json
vendored
Normal file
75
testdata/ClientHello-JSON-Chrome102.json
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
{
|
||||
"cipher_suites": [
|
||||
{"id": 2570, "description": "GREASE"},
|
||||
{"id": 4865, "description": "TLS_AES_128_GCM_SHA256"},
|
||||
{"id": 4866, "description": "TLS_AES_256_GCM_SHA384"},
|
||||
{"id": 4867, "description": "TLS_CHACHA20_POLY1305_SHA256"},
|
||||
{"id": 49195, "description": "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"},
|
||||
{"id": 49199, "description": "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"},
|
||||
{"id": 49196, "description": "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"},
|
||||
{"id": 49200, "description": "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"},
|
||||
{"id": 52393, "description": "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305"},
|
||||
{"id": 52392, "description": "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305"},
|
||||
{"id": 49171, "description": "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"},
|
||||
{"id": 49172, "description": "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"},
|
||||
{"id": 156, "description": "TLS_RSA_WITH_AES_128_GCM_SHA256"},
|
||||
{"id": 157, "description": "TLS_RSA_WITH_AES_256_GCM_SHA384"},
|
||||
{"id": 47, "description": "TLS_RSA_WITH_AES_128_CBC_SHA"},
|
||||
{"id": 53, "description": "TLS_RSA_WITH_AES_256_CBC_SHA"}
|
||||
],
|
||||
"compression_methods": [
|
||||
{"id": 0, "description": "null"}
|
||||
],
|
||||
"extensions": [
|
||||
{"id": 31354, "description": "GREASE"},
|
||||
{"id": 0, "description": "server_name"},
|
||||
{"id": 23, "description": "extended_master_secret"},
|
||||
{"id": 65281, "description": "renegotiation_info", "renegotiated_connection": []},
|
||||
{"id": 10, "description": "supported_groups", "named_group_list": [
|
||||
{"id": 6682, "description": "GREASE"},
|
||||
{"id": 29, "description": "x25519"},
|
||||
{"id": 23, "description": "secp256r1"},
|
||||
{"id": 24, "description": "secp384r1"}
|
||||
]},
|
||||
{"id": 11, "description": "ec_point_formats", "ec_point_format_list": [
|
||||
{"id": 0, "description": "uncompressed"}
|
||||
]},
|
||||
{"id": 35, "description": "session_ticket"},
|
||||
{"id": 16, "description": "application_layer_protocol_negotiation", "protocol_name_list": [
|
||||
"h2",
|
||||
"http/1.1"
|
||||
]},
|
||||
{"id": 5, "description": "status_request"},
|
||||
{"id": 13, "description": "signature_algorithms", "supported_signature_algorithms": [
|
||||
{"id": 1027, "description": "ecdsa_secp256r1_sha256"},
|
||||
{"id": 2052, "description": "rsa_pss_rsae_sha256"},
|
||||
{"id": 1025, "description": "rsa_pkcs1_sha256"},
|
||||
{"id": 1283, "description": "ecdsa_secp384r1_sha384"},
|
||||
{"id": 2053, "description": "rsa_pss_rsae_sha384"},
|
||||
{"id": 1281, "description": "rsa_pkcs1_sha384"},
|
||||
{"id": 2054, "description": "rsa_pss_rsae_sha512"},
|
||||
{"id": 1537, "description": "rsa_pkcs1_sha512"}
|
||||
]},
|
||||
{"id": 18, "description": "signed_certificate_timestamp"},
|
||||
{"id": 51, "description": "key_share", "client_shares": [
|
||||
{"group": 10794, "description": "GREASE", "key_exchange": [0]},
|
||||
{"group": 29, "description": "x25519"}
|
||||
]},
|
||||
{"id": 45, "description": "psk_key_exchange_modes", "ke_modes": [
|
||||
{"mode": 1, "description": "psk_dhe_ke"}
|
||||
]},
|
||||
{"id": 43, "description": "supported_versions", "versions": [
|
||||
{"version": 14906, "description": "GREASE"},
|
||||
{"version": 772, "description": "TLSv1.3"},
|
||||
{"version": 771, "description": "TLSv1.2"}
|
||||
]},
|
||||
{"id": 27, "description": "compress_certificate", "algorithms": [
|
||||
{"algorithm": 2, "description": "brotli"}
|
||||
]},
|
||||
{"id": 17513, "description": "application_settings", "supported_protocols": [
|
||||
"h2"
|
||||
]},
|
||||
{"id": 19018, "description": "GREASE"},
|
||||
{"id": 21, "description": "padding", "len": 0}
|
||||
]
|
||||
}
|
|
@ -16,6 +16,16 @@ type ClientHelloSpecJSONUnmarshaler struct {
|
|||
TLSVersMax uint16 `json:"max_vers,omitempty"` // optional
|
||||
}
|
||||
|
||||
func (chsju *ClientHelloSpecJSONUnmarshaler) ClientHelloSpec() ClientHelloSpec {
|
||||
return ClientHelloSpec{
|
||||
CipherSuites: chsju.CipherSuites.CipherSuites(),
|
||||
CompressionMethods: chsju.CompressionMethods.CompressionMethods(),
|
||||
Extensions: chsju.Extensions.Extensions(),
|
||||
TLSVersMin: chsju.TLSVersMin,
|
||||
TLSVersMax: chsju.TLSVersMax,
|
||||
}
|
||||
}
|
||||
|
||||
type CipherSuitesJSONUnmarshaler struct {
|
||||
cipherSuites []uint16
|
||||
}
|
||||
|
@ -38,6 +48,10 @@ func (c *CipherSuitesJSONUnmarshaler) UnmarshalJSON(jsonStr []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *CipherSuitesJSONUnmarshaler) CipherSuites() []uint16 {
|
||||
return c.cipherSuites
|
||||
}
|
||||
|
||||
type CompressionMethodsJSONUnmarshaler struct {
|
||||
compressionMethods []uint8
|
||||
}
|
||||
|
@ -60,6 +74,10 @@ func (c *CompressionMethodsJSONUnmarshaler) UnmarshalJSON(jsonStr []byte) error
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *CompressionMethodsJSONUnmarshaler) CompressionMethods() []uint8 {
|
||||
return c.compressionMethods
|
||||
}
|
||||
|
||||
type TLSExtensionsJSONUnmarshaler struct {
|
||||
extensions []TLSExtensionJSON
|
||||
}
|
||||
|
@ -72,8 +90,8 @@ func (e *TLSExtensionsJSONUnmarshaler) UnmarshalJSON(jsonStr []byte) error {
|
|||
|
||||
var exts []TLSExtensionJSON = make([]TLSExtensionJSON, 0, len(accepters))
|
||||
for _, accepter := range accepters {
|
||||
var extID uint16 = accepter.idNameObj.ID
|
||||
var extName string = accepter.idNameObj.Name
|
||||
var extID uint16 = accepter.idDescObj.ID
|
||||
var extName string = accepter.idDescObj.Description
|
||||
|
||||
// get extension type from ID
|
||||
var ext TLSExtension = ExtensionFromID(extID)
|
||||
|
@ -101,6 +119,14 @@ func (e *TLSExtensionsJSONUnmarshaler) UnmarshalJSON(jsonStr []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (e *TLSExtensionsJSONUnmarshaler) Extensions() []TLSExtension {
|
||||
var exts []TLSExtension = make([]TLSExtension, 0, len(e.extensions))
|
||||
for _, ext := range e.extensions {
|
||||
exts = append(exts, ext)
|
||||
}
|
||||
return exts
|
||||
}
|
||||
|
||||
func genericExtension(id uint16, name string) TLSExtension {
|
||||
var warningMsg string = "WARNING: extension "
|
||||
warningMsg += fmt.Sprintf("%d ", id)
|
||||
|
@ -115,9 +141,9 @@ func genericExtension(id uint16, name string) TLSExtension {
|
|||
}
|
||||
|
||||
type tlsExtensionJSONAccepter struct {
|
||||
idNameObj struct {
|
||||
ID uint16 `json:"id"`
|
||||
Name string `json:"name,omitempty"`
|
||||
idDescObj struct {
|
||||
ID uint16 `json:"id"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
jsonStr []byte
|
||||
}
|
||||
|
@ -125,44 +151,5 @@ type tlsExtensionJSONAccepter struct {
|
|||
func (t *tlsExtensionJSONAccepter) UnmarshalJSON(jsonStr []byte) error {
|
||||
t.jsonStr = make([]byte, len(jsonStr))
|
||||
copy(t.jsonStr, jsonStr)
|
||||
return json.Unmarshal(jsonStr, &t.idNameObj)
|
||||
return json.Unmarshal(jsonStr, &t.idDescObj)
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
"cipher_suites": [
|
||||
{"id": 0x1301, "name": "TLS_AES_128_GCM_SHA256"},
|
||||
{"id": 0x1302, "name": "TLS_AES_256_GCM_SHA384"}
|
||||
],
|
||||
"compression_methods": [
|
||||
{"id": 0x00, "name": "null"}
|
||||
],
|
||||
"extensions": [
|
||||
{"id": 0x7a7a, "name": "GREASE", "data": []}, // grease, id could be any 0xNaNa where N in 0~f, data is an optional byte slice
|
||||
{"id": 0x0000, "name": "server_name"}, // SNI may(should) have data but will be ignored
|
||||
{"id": 0x0017, "name": "extended_master_secret"}, // always no data
|
||||
{"id": 0xff01, "name": "renegotiation_info", "renegotiated_connection": []}, // no data for initial ClientHello
|
||||
{"id": 0x000a, "name": "supported_groups", "named_group_list": [
|
||||
{"id": 0x1a1a, "name": "GREASE"},
|
||||
{"id": 0x001d, "name": "x25519"},
|
||||
{"id": 0x0017, "name": "secp256r1"},
|
||||
{"id": 0x0018, "name": "secp384r1"}
|
||||
]},
|
||||
{"id": 0x000b, "name": "ec_point_formats", "ec_point_format_list": [
|
||||
{"id": 0x00, "name": "uncompressed"},
|
||||
]},
|
||||
{"id": 0x0023, "name": "session_ticket"}, // always no data
|
||||
{"id": 0x0010, "name": "application_layer_protocol_negotiation", "protocol_name_list": [
|
||||
"h2",
|
||||
"http/1.1"
|
||||
]},
|
||||
{"id": 0x0005, "name": "status_request"}, // always no data
|
||||
{"id": 0x000d, "name": "signature_algorithms", "supported_signature_algorithms": [
|
||||
{"id": 0x0403, "name": "ecdsa_secp256r1_sha256"},
|
||||
{"id": 0x0804, "name": "rsa_pss_rsae_sha256"},
|
||||
...
|
||||
]},
|
||||
...
|
||||
]
|
||||
}
|
||||
*/
|
93
u_clienthello_json_test.go
Normal file
93
u_clienthello_json_test.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
package tls
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestClientHelloSpecJSONUnmarshaler(t *testing.T) {
|
||||
testClientHelloSpecJSONUnmarshaler_Chrome102(t)
|
||||
}
|
||||
|
||||
func testClientHelloSpecJSONUnmarshaler_Chrome102(t *testing.T) {
|
||||
jsonCH, err := os.ReadFile("testdata/ClientHello-JSON-Chrome102.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var chsju ClientHelloSpecJSONUnmarshaler
|
||||
if err := json.Unmarshal(jsonCH, &chsju); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
savedChrome102, _ := utlsIdToSpec(HelloChrome_102)
|
||||
jsonCHS := chsju.ClientHelloSpec()
|
||||
|
||||
// Compare CipherSuites
|
||||
if !reflect.DeepEqual(jsonCHS.CipherSuites, savedChrome102.CipherSuites) {
|
||||
t.Errorf("got %#v, want %#v", jsonCHS.CipherSuites, savedChrome102.CipherSuites)
|
||||
}
|
||||
|
||||
// Compare CompressionMethods
|
||||
if !reflect.DeepEqual(jsonCHS.CompressionMethods, savedChrome102.CompressionMethods) {
|
||||
t.Errorf("got %#v, want %#v", jsonCHS.CompressionMethods, savedChrome102.CompressionMethods)
|
||||
}
|
||||
|
||||
// Compare Extensions
|
||||
if len(jsonCHS.Extensions) != len(savedChrome102.Extensions) {
|
||||
t.Errorf("len(jsonCHS.Extensions) = %d != %d = len(savedChrome102.Extensions)", len(jsonCHS.Extensions), len(savedChrome102.Extensions))
|
||||
}
|
||||
|
||||
for i := range jsonCHS.Extensions {
|
||||
if !reflect.DeepEqual(jsonCHS.Extensions[i], savedChrome102.Extensions[i]) {
|
||||
if _, ok := jsonCHS.Extensions[i].(*UtlsPaddingExtension); ok {
|
||||
continue // UtlsPaddingExtension has non-nil function member
|
||||
}
|
||||
t.Errorf("got %#v, want %#v", jsonCHS.Extensions[i], savedChrome102.Extensions[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientHelloSpecUnmarshalJSON(t *testing.T) {
|
||||
testClientHelloSpecUnmarshalJSON_Chrome102(t)
|
||||
}
|
||||
|
||||
func testClientHelloSpecUnmarshalJSON_Chrome102(t *testing.T) {
|
||||
var chs ClientHelloSpec
|
||||
jsonCH, err := os.ReadFile("testdata/ClientHello-JSON-Chrome102.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(jsonCH, &chs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
savedChrome102, _ := utlsIdToSpec(HelloChrome_102)
|
||||
|
||||
// Compare CipherSuites
|
||||
if !reflect.DeepEqual(chs.CipherSuites, savedChrome102.CipherSuites) {
|
||||
t.Errorf("got %#v, want %#v", chs.CipherSuites, savedChrome102.CipherSuites)
|
||||
}
|
||||
|
||||
// Compare CompressionMethods
|
||||
if !reflect.DeepEqual(chs.CompressionMethods, savedChrome102.CompressionMethods) {
|
||||
t.Errorf("got %#v, want %#v", chs.CompressionMethods, savedChrome102.CompressionMethods)
|
||||
}
|
||||
|
||||
// Compare Extensions
|
||||
if len(chs.Extensions) != len(savedChrome102.Extensions) {
|
||||
t.Errorf("len(chs.Extensions) = %d != %d = len(savedChrome102.Extensions)", len(chs.Extensions), len(savedChrome102.Extensions))
|
||||
}
|
||||
|
||||
for i := range chs.Extensions {
|
||||
if !reflect.DeepEqual(chs.Extensions[i], savedChrome102.Extensions[i]) {
|
||||
if _, ok := chs.Extensions[i].(*UtlsPaddingExtension); ok {
|
||||
continue // UtlsPaddingExtension has non-nil function member
|
||||
}
|
||||
t.Errorf("got %#v, want %#v", chs.Extensions[i], savedChrome102.Extensions[i])
|
||||
}
|
||||
}
|
||||
}
|
|
@ -523,7 +523,13 @@ func (chs *ClientHelloSpec) FromRaw(raw []byte, allowBluntMimicry ...bool) error
|
|||
|
||||
// UnmarshalJSON unmarshals a ClientHello message in the form of JSON into a ClientHelloSpec.
|
||||
func (chs *ClientHelloSpec) UnmarshalJSON(jsonB []byte) error {
|
||||
return errors.New("unimplemented")
|
||||
var chsju ClientHelloSpecJSONUnmarshaler
|
||||
if err := json.Unmarshal(jsonB, &chsju); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*chs = chsju.ClientHelloSpec()
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
|
@ -540,8 +540,8 @@ func (fh *finishedHash) getPublicObj() FinishedHash {
|
|||
|
||||
// TLS 1.3 Key Share. See RFC 8446, Section 4.2.8.
|
||||
type KeyShare struct {
|
||||
Group CurveID
|
||||
Data []byte
|
||||
Group CurveID `json:"group"`
|
||||
Data []byte `json:"key_exchange,omitempty"` // optional
|
||||
}
|
||||
|
||||
type KeyShares []KeyShare
|
||||
|
|
|
@ -379,17 +379,16 @@ func (e *SupportedCurvesExtension) Write(b []byte) (int, error) {
|
|||
}
|
||||
|
||||
func (e *SupportedCurvesExtension) UnmarshalJSON(data []byte) error {
|
||||
var namedGroupList struct {
|
||||
NamedGroups []struct {
|
||||
ID uint16 `json:"id"`
|
||||
Name string `json:"name,omitempty"`
|
||||
var namedGroups struct {
|
||||
NamedGroupList []struct {
|
||||
ID uint16 `json:"id"`
|
||||
} `json:"named_group_list"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &namedGroupList); err != nil {
|
||||
if err := json.Unmarshal(data, &namedGroups); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, namedGroup := range namedGroupList.NamedGroups {
|
||||
for _, namedGroup := range namedGroups.NamedGroupList {
|
||||
e.Curves = append(e.Curves, CurveID(unGREASEUint16(namedGroup.ID)))
|
||||
}
|
||||
return nil
|
||||
|
@ -426,16 +425,15 @@ func (e *SupportedPointsExtension) Read(b []byte) (int, error) {
|
|||
|
||||
func (e *SupportedPointsExtension) UnmarshalJSON(data []byte) error {
|
||||
var pointFormatList struct {
|
||||
PointFormats []struct {
|
||||
ID uint8 `json:"id"`
|
||||
Name string `json:"name,omitempty"`
|
||||
ECPointFormatList []struct {
|
||||
ID uint8 `json:"id"`
|
||||
} `json:"ec_point_format_list"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &pointFormatList); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, pointFormat := range pointFormatList.PointFormats {
|
||||
for _, pointFormat := range pointFormatList.ECPointFormatList {
|
||||
e.SupportedPoints = append(e.SupportedPoints, pointFormat.ID)
|
||||
}
|
||||
return nil
|
||||
|
@ -506,6 +504,22 @@ func (e *SignatureAlgorithmsExtension) Write(b []byte) (int, error) {
|
|||
return fullLen, nil
|
||||
}
|
||||
|
||||
func (e *SignatureAlgorithmsExtension) UnmarshalJSON(data []byte) error {
|
||||
var signatureAlgorithms struct {
|
||||
SignatureAlgorithms []struct {
|
||||
ID uint16 `json:"id"`
|
||||
} `json:"supported_signature_algorithms"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &signatureAlgorithms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, sigAndHash := range signatureAlgorithms.SignatureAlgorithms {
|
||||
e.SupportedSignatureAlgorithms = append(e.SupportedSignatureAlgorithms, SignatureScheme(unGREASEUint16(sigAndHash.ID)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SignatureAlgorithmsCertExtension struct {
|
||||
SupportedSignatureAlgorithms []SignatureScheme
|
||||
}
|
||||
|
@ -695,15 +709,15 @@ func (e *ALPNExtension) Write(b []byte) (int, error) {
|
|||
}
|
||||
|
||||
func (e *ALPNExtension) UnmarshalJSON(b []byte) error {
|
||||
var protocolNameList struct {
|
||||
ProtocolNames []string `json:"protocol_name_list"`
|
||||
var protocolNames struct {
|
||||
ProtocolNameList []string `json:"protocol_name_list"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(b, &protocolNameList); err != nil {
|
||||
if err := json.Unmarshal(b, &protocolNames); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.AlpnProtocols = protocolNameList.ProtocolNames
|
||||
e.AlpnProtocols = protocolNames.ProtocolNameList
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -778,6 +792,20 @@ func (e *ApplicationSettingsExtension) Write(b []byte) (int, error) {
|
|||
return fullLen, nil
|
||||
}
|
||||
|
||||
func (e *ApplicationSettingsExtension) UnmarshalJSON(b []byte) error {
|
||||
var applicationSettingsSupport struct {
|
||||
SupportedProtocols []string `json:"supported_protocols"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(b, &applicationSettingsSupport); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.SupportedProtocols = applicationSettingsSupport.SupportedProtocols
|
||||
return nil
|
||||
}
|
||||
|
||||
// SCTExtension stands for SignedCertificateTimestamp
|
||||
type SCTExtension struct {
|
||||
}
|
||||
|
||||
|
@ -805,6 +833,10 @@ func (e *SCTExtension) Write(_ []byte) (int, error) {
|
|||
return 0, nil
|
||||
}
|
||||
|
||||
func (e *SCTExtension) UnmarshalJSON(_ []byte) error {
|
||||
return nil // no-op
|
||||
}
|
||||
|
||||
type SessionTicketExtension struct {
|
||||
Session *ClientSessionState
|
||||
}
|
||||
|
@ -984,8 +1016,10 @@ func (e *UtlsGREASEExtension) Write(b []byte) (int, error) {
|
|||
|
||||
func (e *UtlsGREASEExtension) UnmarshalJSON(b []byte) error {
|
||||
var jsonObj struct {
|
||||
Id uint16 `json:"id"`
|
||||
Data []byte `json:"data"`
|
||||
Id uint16 `json:"id"`
|
||||
Data []byte `json:"data"`
|
||||
KeepID bool `json:"keep_id"`
|
||||
KeepData bool `json:"keep_data"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(b, &jsonObj); err != nil {
|
||||
|
@ -993,8 +1027,12 @@ func (e *UtlsGREASEExtension) UnmarshalJSON(b []byte) error {
|
|||
}
|
||||
|
||||
if isGREASEUint16(jsonObj.Id) {
|
||||
e.Value = GREASE_PLACEHOLDER
|
||||
e.Body = jsonObj.Data
|
||||
if jsonObj.KeepID {
|
||||
e.Value = jsonObj.Id
|
||||
}
|
||||
if jsonObj.KeepData {
|
||||
e.Body = jsonObj.Data
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return errors.New("GREASE extension id must be a GREASE value")
|
||||
|
@ -1048,6 +1086,24 @@ func (e *UtlsPaddingExtension) Write(_ []byte) (int, error) {
|
|||
return 0, nil
|
||||
}
|
||||
|
||||
func (e *UtlsPaddingExtension) UnmarshalJSON(b []byte) error {
|
||||
var jsonObj struct {
|
||||
Length uint `json:"len"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &jsonObj); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if jsonObj.Length == 0 {
|
||||
e.GetPaddingLen = BoringPaddingStyle
|
||||
} else {
|
||||
e.PaddingLen = int(jsonObj.Length)
|
||||
e.WillPad = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// https://github.com/google/boringssl/blob/7d7554b6b3c79e707e25521e61e066ce2b996e4c/ssl/t1_lib.c#L2803
|
||||
func BoringPaddingStyle(unpaddedLen int) (int, bool) {
|
||||
if unpaddedLen > 0xff && unpaddedLen < 0x200 {
|
||||
|
@ -1127,6 +1183,24 @@ func (e *UtlsCompressCertExtension) Write(b []byte) (int, error) {
|
|||
return fullLen, nil
|
||||
}
|
||||
|
||||
func (e *UtlsCompressCertExtension) UnmarshalJSON(b []byte) error {
|
||||
var certificateCompressionAlgorithms struct {
|
||||
Algorithms []struct {
|
||||
Algorithm uint16 `json:"algorithm"`
|
||||
} `json:"algorithms"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &certificateCompressionAlgorithms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
algorithms := []CertCompressionAlgo{}
|
||||
for _, algo := range certificateCompressionAlgorithms.Algorithms {
|
||||
algorithms = append(algorithms, CertCompressionAlgo(algo.Algorithm))
|
||||
}
|
||||
e.Algorithms = algorithms
|
||||
return nil
|
||||
}
|
||||
|
||||
/* TLS 1.3 */
|
||||
type KeyShareExtension struct {
|
||||
KeyShares []KeyShare
|
||||
|
@ -1204,6 +1278,22 @@ func (e *KeyShareExtension) writeToUConn(uc *UConn) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (e *KeyShareExtension) UnmarshalJSON(b []byte) error {
|
||||
var keyShareClientHello struct {
|
||||
ClientShares []KeyShare `json:"client_shares"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &keyShareClientHello); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range keyShareClientHello.ClientShares {
|
||||
keyShareClientHello.ClientShares[i].Group = CurveID(unGREASEUint16(uint16(keyShareClientHello.ClientShares[i].Group)))
|
||||
}
|
||||
e.KeyShares = make([]KeyShare, len(keyShareClientHello.ClientShares))
|
||||
copy(e.KeyShares, keyShareClientHello.ClientShares)
|
||||
return nil
|
||||
}
|
||||
|
||||
type PSKKeyExchangeModesExtension struct {
|
||||
Modes []uint8
|
||||
}
|
||||
|
@ -1257,6 +1347,22 @@ func (e *PSKKeyExchangeModesExtension) writeToUConn(uc *UConn) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (e *PSKKeyExchangeModesExtension) UnmarshalJSON(b []byte) error {
|
||||
var pskKeyExchangeModes struct {
|
||||
Modes []struct {
|
||||
Mode uint8 `json:"mode"`
|
||||
} `json:"ke_modes"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &pskKeyExchangeModes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, mode := range pskKeyExchangeModes.Modes {
|
||||
e.Modes = append(e.Modes, mode.Mode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SupportedVersionsExtension struct {
|
||||
Versions []uint16
|
||||
}
|
||||
|
@ -1314,6 +1420,22 @@ func (e *SupportedVersionsExtension) Write(b []byte) (int, error) {
|
|||
return fullLen, nil
|
||||
}
|
||||
|
||||
func (e *SupportedVersionsExtension) UnmarshalJSON(b []byte) error {
|
||||
var supportedVersions struct {
|
||||
Versions []struct {
|
||||
Version uint16 `json:"version"`
|
||||
} `json:"versions"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &supportedVersions); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, version := range supportedVersions.Versions {
|
||||
e.Versions = append(e.Versions, unGREASEUint16(version.Version))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MUST NOT be part of initial ClientHello
|
||||
type CookieExtension struct {
|
||||
Cookie []byte
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue