diff --git a/common.go b/common.go index b3d7f89b..a79cccc2 100644 --- a/common.go +++ b/common.go @@ -89,6 +89,7 @@ const ( extensionSupportedPoints uint16 = 11 extensionSignatureAlgorithms uint16 = 13 extensionALPN uint16 = 16 + extensionStatusRequestV2 uint16 = 17 extensionSCT uint16 = 18 extensionDelegatedCredentials uint16 = 34 extensionSessionTicket uint16 = 35 @@ -100,7 +101,7 @@ const ( extensionCertificateAuthorities uint16 = 47 extensionSignatureAlgorithmsCert uint16 = 50 extensionKeyShare uint16 = 51 - extensionNextProtoNeg uint16 = 13172 // not IANA assigned // Pending discussion on whether or not remove this. crypto/tls removed it on Nov 21, 2019. + extensionNextProtoNeg uint16 = 13172 // not IANA assigned // Pending discussion on whether or not remove this. crypto/tls removed it on Nov 21, 2019. extensionRenegotiationInfo uint16 = 0xff01 ) @@ -237,6 +238,10 @@ type ConnectionState struct { // Deprecated: this value is always true. NegotiatedProtocolIsMutual bool + // PeerApplicationSettings is the Application-Layer Protocol Settings (ALPS) + // provided by peer. + PeerApplicationSettings []byte // [uTLS] + // ServerName is the value of the Server Name Indication extension sent by // the client. It's available both on the server and on the client side. ServerName string @@ -624,6 +629,10 @@ type Config struct { // ConnectionState.NegotiatedProtocol will be empty. NextProtos []string + // ApplicationSettings is a set of application settings (ALPS) to use + // with each application protocol (ALPN). + ApplicationSettings map[string][]byte // [uTLS] + // ServerName is used to verify the hostname on the returned // certificates unless InsecureSkipVerify is given. It is also included // in the client's handshake to support virtual hosting unless it is @@ -799,6 +808,7 @@ func (c *Config) Clone() *Config { VerifyConnection: c.VerifyConnection, RootCAs: c.RootCAs, NextProtos: c.NextProtos, + ApplicationSettings: c.ApplicationSettings, ServerName: c.ServerName, ClientAuth: c.ClientAuth, ClientCAs: c.ClientCAs, diff --git a/conn.go b/conn.go index 805354b7..13a7963f 100644 --- a/conn.go +++ b/conn.go @@ -92,6 +92,10 @@ type Conn struct { // clientProtocol is the negotiated ALPN protocol. clientProtocol string + // [UTLS SECTION START] + utls utlsConnExtraFields // used for extensive things such as ALPS + // [UTLS SECTION END] + // input/output in, out halfConn rawInput bytes.Buffer // raw input, starting with a record header @@ -1075,17 +1079,22 @@ func (c *Conn) readHandshake() (any, error) { } case typeFinished: m = new(finishedMsg) - case typeEncryptedExtensions: - m = new(encryptedExtensionsMsg) + // [uTLS] Commented typeEncryptedExtensions to force + // utlsHandshakeMessageType to handle it + // case typeEncryptedExtensions: + // m = new(encryptedExtensionsMsg) case typeEndOfEarlyData: m = new(endOfEarlyDataMsg) case typeKeyUpdate: m = new(keyUpdateMsg) - // [UTLS SECTION BEGINS] - case typeCompressedCertificate: - m = new(compressedCertificateMsg) - // [UTLS SECTION ENDS] default: + // [UTLS SECTION BEGINS] + var err error + m, err = c.utlsHandshakeMessageType(data[0]) // see u_conn.go + if err == nil { + break + } + // [UTLS SECTION ENDS] return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) } @@ -1514,6 +1523,7 @@ func (c *Conn) connectionStateLocked() ConnectionState { } else { state.ekm = c.ekm } + return state } diff --git a/handshake_client_tls13.go b/handshake_client_tls13.go index 47aa2026..cddf081a 100644 --- a/handshake_client_tls13.go +++ b/handshake_client_tls13.go @@ -6,7 +6,6 @@ package tls import ( "bytes" - "compress/zlib" "context" "crypto" "crypto/hmac" @@ -14,12 +13,8 @@ import ( "errors" "fmt" "hash" - "io" "sync/atomic" "time" - - "github.com/andybalholm/brotli" - "github.com/klauspost/compress/zstd" ) type clientHandshakeStateTLS13 struct { @@ -103,6 +98,11 @@ func (hs *clientHandshakeStateTLS13) handshake() error { if err := hs.readServerFinished(); err != nil { return err } + // [UTLS SECTION START] + if err := hs.serverFinishedReceived(); err != nil { + return err + } + // [UTLS SECTION END] if err := hs.sendClientCertificate(); err != nil { return err } @@ -477,6 +477,15 @@ func (hs *clientHandshakeStateTLS13) readServerParameters() error { } c.clientProtocol = encryptedExtensions.alpnProtocol + // [UTLS SECTION STARTS] + if hs.uconn != nil { + err = hs.utlsReadServerParameters(encryptedExtensions) + if err != nil { + c.sendAlert(alertUnsupportedExtension) + return err + } + } + // [UTLS SECTION ENDS] return nil } @@ -516,19 +525,15 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error { } // [UTLS SECTION BEGINS] - receivedCompressedCert := false - // Check to see if we advertised any compression algorithms - if hs.uconn != nil && len(hs.uconn.certCompressionAlgs) > 0 { - // Check to see if the message is a compressed certificate message, otherwise move on. - compressedCertMsg, ok := msg.(*compressedCertificateMsg) - if ok { - receivedCompressedCert = true - hs.transcript.Write(compressedCertMsg.marshal()) - - msg, err = hs.decompressCert(*compressedCertMsg) - if err != nil { - return fmt.Errorf("tls: failed to decompress certificate message: %w", err) - } + var skipWritingCertToTranscript bool = false + if hs.uconn != nil { + processedMsg, err := hs.utlsReadServerCertificate(msg) + if err != nil { + return err + } + if processedMsg != nil { + skipWritingCertToTranscript = true + msg = processedMsg // msg is now a processed-by-extension certificateMsg } } // [UTLS SECTION ENDS] @@ -544,7 +549,7 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error { } // [UTLS SECTION BEGINS] // Previously, this was simply 'hs.transcript.Write(certMsg.marshal())' (without the if). - if !receivedCompressedCert { + if !skipWritingCertToTranscript { hs.transcript.Write(certMsg.marshal()) } // [UTLS SECTION ENDS] @@ -570,7 +575,7 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error { // See RFC 8446, Section 4.4.3. if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) { c.sendAlert(alertIllegalParameter) - return errors.New("tls: certificate used with invalid signature algorithm") + return errors.New("tls: certificate used with invalid signature algorithm -- not implemented") } sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm) if err != nil { @@ -578,7 +583,7 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error { } if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 { c.sendAlert(alertIllegalParameter) - return errors.New("tls: certificate used with invalid signature algorithm") + return errors.New("tls: certificate used with invalid signature algorithm -- obsolete") } signed := signedMessage(sigHash, serverSignatureContext, hs.transcript) if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey, @@ -729,80 +734,6 @@ func (hs *clientHandshakeStateTLS13) sendClientFinished() error { return nil } -// [UTLS SECTION BEGINS] -func (hs *clientHandshakeStateTLS13) decompressCert(m compressedCertificateMsg) (*certificateMsgTLS13, error) { - var ( - decompressed io.Reader - compressed = bytes.NewReader(m.compressedCertificateMessage) - c = hs.c - ) - - // Check to see if the peer responded with an algorithm we advertised. - supportedAlg := false - for _, alg := range hs.uconn.certCompressionAlgs { - if m.algorithm == uint16(alg) { - supportedAlg = true - } - } - if !supportedAlg { - c.sendAlert(alertBadCertificate) - return nil, fmt.Errorf("unadvertised algorithm (%d)", m.algorithm) - } - - switch CertCompressionAlgo(m.algorithm) { - case CertCompressionBrotli: - decompressed = brotli.NewReader(compressed) - - case CertCompressionZlib: - rc, err := zlib.NewReader(compressed) - if err != nil { - c.sendAlert(alertBadCertificate) - return nil, fmt.Errorf("failed to open zlib reader: %w", err) - } - defer rc.Close() - decompressed = rc - - case CertCompressionZstd: - rc, err := zstd.NewReader(compressed) - if err != nil { - c.sendAlert(alertBadCertificate) - return nil, fmt.Errorf("failed to open zstd reader: %w", err) - } - defer rc.Close() - decompressed = rc - - default: - c.sendAlert(alertBadCertificate) - return nil, fmt.Errorf("unsupported algorithm (%d)", m.algorithm) - } - - rawMsg := make([]byte, m.uncompressedLength+4) // +4 for message type and uint24 length field - rawMsg[0] = typeCertificate - rawMsg[1] = uint8(m.uncompressedLength >> 16) - rawMsg[2] = uint8(m.uncompressedLength >> 8) - rawMsg[3] = uint8(m.uncompressedLength) - - n, err := decompressed.Read(rawMsg[4:]) - if err != nil { - c.sendAlert(alertBadCertificate) - return nil, err - } - if n < len(rawMsg)-4 { - // If, after decompression, the specified length does not match the actual length, the party - // receiving the invalid message MUST abort the connection with the "bad_certificate" alert. - // https://datatracker.ietf.org/doc/html/rfc8879#section-4 - c.sendAlert(alertBadCertificate) - return nil, fmt.Errorf("decompressed len (%d) does not match specified len (%d)", n, m.uncompressedLength) - } - certMsg := new(certificateMsgTLS13) - if !certMsg.unmarshal(rawMsg) { - return nil, c.sendAlert(alertUnexpectedMessage) - } - return certMsg, nil -} - -// [UTLS SECTION ENDS] - func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error { if !c.isClient { c.sendAlert(alertUnexpectedMessage) diff --git a/handshake_messages.go b/handshake_messages.go index 6daf19c5..6d81cc7a 100644 --- a/handshake_messages.go +++ b/handshake_messages.go @@ -868,6 +868,8 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool { type encryptedExtensionsMsg struct { raw []byte alpnProtocol string + + utls utlsEncryptedExtensionsMsgExtraFields // [uTLS] } func (m *encryptedExtensionsMsg) marshal() []byte { @@ -927,6 +929,11 @@ func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool { } m.alpnProtocol = string(proto) default: + // [UTLS SECTION START] + if !m.utlsUnmarshal(extension, extData) { + return false // return false when ERROR + } + // [UTLS SECTION END] // Ignore unknown extensions. continue } diff --git a/handshake_messages_test.go b/handshake_messages_test.go index 93ac1b1c..5597a63c 100644 --- a/handshake_messages_test.go +++ b/handshake_messages_test.go @@ -36,7 +36,7 @@ var tests = []any{ &newSessionTicketMsgTLS13{}, &certificateRequestMsgTLS13{}, &certificateMsgTLS13{}, - &compressedCertificateMsg{}, // [UTLS] + &utlsCompressedCertificateMsg{}, // [UTLS] } func TestMarshalUnmarshal(t *testing.T) { @@ -406,8 +406,8 @@ func (*certificateMsgTLS13) Generate(rand *rand.Rand, size int) reflect.Value { } // [UTLS] -func (*compressedCertificateMsg) Generate(rand *rand.Rand, size int) reflect.Value { - m := &compressedCertificateMsg{} +func (*utlsCompressedCertificateMsg) Generate(rand *rand.Rand, size int) reflect.Value { + m := &utlsCompressedCertificateMsg{} m.algorithm = uint16(rand.Intn(2 << 15)) m.uncompressedLength = uint32(rand.Intn(2 << 23)) m.compressedCertificateMessage = randomBytes(rand.Intn(500)+1, rand) diff --git a/key_agreement.go b/key_agreement.go index c28a64f3..75deeb01 100644 --- a/key_agreement.go +++ b/key_agreement.go @@ -319,7 +319,7 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell } if !isSupportedSignatureAlgorithm(signatureAlgorithm, clientHello.supportedSignatureAlgorithms) { - return errors.New("tls: certificate used with invalid signature algorithm") + return fmt.Errorf("tls: certificate used with invalid signature algorithm -- ClientHello not advertising %04x", uint16(signatureAlgorithm)) } sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm) if err != nil { diff --git a/tls_test.go b/tls_test.go index e3e8b8c2..f2b1b64e 100644 --- a/tls_test.go +++ b/tls_test.go @@ -12,7 +12,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/refraction-networking/utls/testenv" "io" "math" "net" @@ -22,6 +21,8 @@ import ( "strings" "testing" "time" + + "github.com/refraction-networking/utls/testenv" ) var rsaCertPEM = `-----BEGIN CERTIFICATE----- @@ -827,6 +828,8 @@ func TestCloneNonFuncFields(t *testing.T) { f.Set(reflect.ValueOf(RenegotiateOnceAsClient)) case "mutex", "autoSessionTicketKeys", "sessionTicketKeys": continue // these are unexported fields that are handled separately + case "ApplicationSettings": + f.Set(reflect.ValueOf(map[string][]byte{"a": {1}})) default: t.Errorf("all fields must be accounted for, but saw unknown field %q", fn) } diff --git a/u_common.go b/u_common.go index 6cfc3f60..0d0a4c33 100644 --- a/u_common.go +++ b/u_common.go @@ -16,24 +16,27 @@ import ( // Things, supported by utls, but not crypto/tls' are prefixed with "utls" // Supported things, that have changed their ID are prefixed with "Old" // Supported but disabled things are prefixed with "Disabled". We will _enable_ them. + +// TLS handshake message types. const ( - utlsExtensionPadding uint16 = 21 - utlsExtensionExtendedMasterSecret uint16 = 23 // https://tools.ietf.org/html/rfc7627 + utlsTypeEncryptedExtensions uint8 = 8 // implemention incomplete by crypto/tls + // https://datatracker.ietf.org/doc/html/rfc8879#section-7.2 + utlsTypeCompressedCertificate uint8 = 25 +) - // https://datatracker.ietf.org/doc/html/rfc8879#section-7.1 - utlsExtensionCompressCertificate uint16 = 27 +// TLS +const ( + utlsExtensionPadding uint16 = 21 + utlsExtensionExtendedMasterSecret uint16 = 23 // https://tools.ietf.org/html/rfc7627 + utlsExtensionCompressCertificate uint16 = 27 // https://datatracker.ietf.org/doc/html/rfc8879#section-7.1 + utlsExtensionApplicationSettings uint16 = 17513 // not IANA assigned + utlsFakeExtensionCustom uint16 = 1234 // not IANA assigned, for ALPS // extensions with 'fake' prefix break connection, if server echoes them back fakeExtensionTokenBinding uint16 = 24 - fakeExtensionChannelIDOld uint16 = 30031 // not IANA assigned + fakeOldExtensionChannelID uint16 = 30031 // not IANA assigned fakeExtensionChannelID uint16 = 30032 // not IANA assigned - fakeExtensionALPS uint16 = 17513 // not IANA assigned fakeExtensionDelegatedCredentials uint16 = 34 - - fakeRecordSizeLimit uint16 = 0x001c - - // https://datatracker.ietf.org/doc/html/rfc8879#section-7.2 - typeCompressedCertificate uint8 = 25 ) const ( @@ -60,6 +63,11 @@ const ( FAKE_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = uint16(0xc008) ) +// Other things +const ( + fakeRecordSizeLimit uint16 = 0x001c +) + // newest signatures var ( FakePKCS1WithSHA224 SignatureScheme = 0x0301 diff --git a/u_conn.go b/u_conn.go index b1feec3a..bf3e0537 100644 --- a/u_conn.go +++ b/u_conn.go @@ -710,3 +710,30 @@ func makeSupportedVersions(minVers, maxVers uint16) []uint16 { } return a } + +// Extending (*Conn).readHandshake() to support more customized handshake messages. +func (c *Conn) utlsHandshakeMessageType(msgType byte) (handshakeMessage, error) { + switch msgType { + case utlsTypeCompressedCertificate: + return new(utlsCompressedCertificateMsg), nil + case utlsTypeEncryptedExtensions: + if c.isClient { + return new(encryptedExtensionsMsg), nil + } else { + return new(utlsClientEncryptedExtensionsMsg), nil + } + default: + return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } +} + +// Extending (*Conn).connectionStateLocked() +func (c *Conn) utlsConnectionStateLocked(state *ConnectionState) { + state.PeerApplicationSettings = c.utls.peerApplicationSettings +} + +type utlsConnExtraFields struct { + hasApplicationSettings bool + peerApplicationSettings []byte + localApplicationSettings []byte +} diff --git a/u_fingerprinter.go b/u_fingerprinter.go index d0c1bcc8..9c9061f9 100644 --- a/u_fingerprinter.go +++ b/u_fingerprinter.go @@ -321,7 +321,7 @@ func (f *Fingerprinter) FingerprintClientHello(data []byte) (*ClientHelloSpec, e case fakeExtensionChannelID: clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &FakeChannelIDExtension{}) - case fakeExtensionChannelIDOld: + case fakeOldExtensionChannelID: clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &FakeChannelIDExtension{true}) case fakeExtensionTokenBinding: @@ -335,7 +335,7 @@ func (f *Fingerprinter) FingerprintClientHello(data []byte) (*ClientHelloSpec, e tokenBindingExt.KeyParameters = keyParameters clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &tokenBindingExt) - case fakeExtensionALPS: + case utlsExtensionApplicationSettings: // Similar to ALPN (RFC 7301, Section 3.1): // https://datatracker.ietf.org/doc/html/draft-vvv-tls-alps#section-3 var protoList cryptobyte.String diff --git a/u_handshake_client.go b/u_handshake_client.go new file mode 100644 index 00000000..6166f6ee --- /dev/null +++ b/u_handshake_client.go @@ -0,0 +1,163 @@ +// Copyright 2022 uTLS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tls + +import ( + "bytes" + "compress/zlib" + "errors" + "fmt" + "io" + + "github.com/andybalholm/brotli" + "github.com/klauspost/compress/zstd" +) + +// This function is called by (*clientHandshakeStateTLS13).readServerCertificate() +// to retrieve the certificate out of a message read by (*Conn).readHandshake() +func (hs *clientHandshakeStateTLS13) utlsReadServerCertificate(msg any) (processedMsg any, err error) { + for _, ext := range hs.uconn.Extensions { + switch ext.(type) { + case *UtlsCompressCertExtension: + // Included Compressed Certificate extension + if len(hs.uconn.certCompressionAlgs) > 0 { + compressedCertMsg, ok := msg.(*utlsCompressedCertificateMsg) + if ok { + hs.transcript.Write(compressedCertMsg.marshal()) + msg, err = hs.decompressCert(*compressedCertMsg) + if err != nil { + return nil, fmt.Errorf("tls: failed to decompress certificate message: %w", err) + } else { + return msg, nil + } + } + } + default: + continue + } + } + return nil, nil +} + +// called by (*clientHandshakeStateTLS13).utlsReadServerCertificate() when UtlsCompressCertExtension is used +func (hs *clientHandshakeStateTLS13) decompressCert(m utlsCompressedCertificateMsg) (*certificateMsgTLS13, error) { + var ( + decompressed io.Reader + compressed = bytes.NewReader(m.compressedCertificateMessage) + c = hs.c + ) + + // Check to see if the peer responded with an algorithm we advertised. + supportedAlg := false + for _, alg := range hs.uconn.certCompressionAlgs { + if m.algorithm == uint16(alg) { + supportedAlg = true + } + } + if !supportedAlg { + c.sendAlert(alertBadCertificate) + return nil, fmt.Errorf("unadvertised algorithm (%d)", m.algorithm) + } + + switch CertCompressionAlgo(m.algorithm) { + case CertCompressionBrotli: + decompressed = brotli.NewReader(compressed) + + case CertCompressionZlib: + rc, err := zlib.NewReader(compressed) + if err != nil { + c.sendAlert(alertBadCertificate) + return nil, fmt.Errorf("failed to open zlib reader: %w", err) + } + defer rc.Close() + decompressed = rc + + case CertCompressionZstd: + rc, err := zstd.NewReader(compressed) + if err != nil { + c.sendAlert(alertBadCertificate) + return nil, fmt.Errorf("failed to open zstd reader: %w", err) + } + defer rc.Close() + decompressed = rc + + default: + c.sendAlert(alertBadCertificate) + return nil, fmt.Errorf("unsupported algorithm (%d)", m.algorithm) + } + + rawMsg := make([]byte, m.uncompressedLength+4) // +4 for message type and uint24 length field + rawMsg[0] = typeCertificate + rawMsg[1] = uint8(m.uncompressedLength >> 16) + rawMsg[2] = uint8(m.uncompressedLength >> 8) + rawMsg[3] = uint8(m.uncompressedLength) + + n, err := decompressed.Read(rawMsg[4:]) + if err != nil { + c.sendAlert(alertBadCertificate) + return nil, err + } + if n < len(rawMsg)-4 { + // If, after decompression, the specified length does not match the actual length, the party + // receiving the invalid message MUST abort the connection with the "bad_certificate" alert. + // https://datatracker.ietf.org/doc/html/rfc8879#section-4 + c.sendAlert(alertBadCertificate) + return nil, fmt.Errorf("decompressed len (%d) does not match specified len (%d)", n, m.uncompressedLength) + } + certMsg := new(certificateMsgTLS13) + if !certMsg.unmarshal(rawMsg) { + return nil, c.sendAlert(alertUnexpectedMessage) + } + return certMsg, nil +} + +// to be called in (*clientHandshakeStateTLS13).handshake(), +// after hs.readServerFinished() and before hs.sendClientCertificate() +func (hs *clientHandshakeStateTLS13) serverFinishedReceived() error { + if err := hs.sendClientEncryptedExtensions(); err != nil { + return err + } + return nil +} + +func (hs *clientHandshakeStateTLS13) sendClientEncryptedExtensions() error { + c := hs.c + clientEncryptedExtensions := new(utlsClientEncryptedExtensionsMsg) + if c.utls.hasApplicationSettings { + clientEncryptedExtensions.hasApplicationSettings = true + clientEncryptedExtensions.applicationSettings = c.utls.localApplicationSettings + hs.transcript.Write(clientEncryptedExtensions.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, clientEncryptedExtensions.marshal()); err != nil { + return err + } + } + + return nil +} + +func (hs *clientHandshakeStateTLS13) utlsReadServerParameters(encryptedExtensions *encryptedExtensionsMsg) error { + hs.c.utls.hasApplicationSettings = encryptedExtensions.utls.hasApplicationSettings + hs.c.utls.peerApplicationSettings = encryptedExtensions.utls.applicationSettings + + if hs.c.utls.hasApplicationSettings { + if hs.uconn.vers < VersionTLS13 { + return errors.New("tls: server sent application settings at invalid version") + } + if len(hs.uconn.clientProtocol) == 0 { + return errors.New("tls: server sent application settings without ALPN") + } + + // Check if the ALPN selected by the server exists in the client's list. + if alps, ok := hs.uconn.config.ApplicationSettings[hs.serverHello.alpnProtocol]; ok { + hs.c.utls.localApplicationSettings = alps + } else { + // return errors.New("tls: server selected ALPN doesn't match a client ALPS") + return nil // ignore if client doesn't have ALPS in use. + // TODO: is this a issue or not? + } + } + + return nil +} diff --git a/u_handshake_messages.go b/u_handshake_messages.go index 07203c21..20f9b4ef 100644 --- a/u_handshake_messages.go +++ b/u_handshake_messages.go @@ -1,3 +1,7 @@ +// Copyright 2022 uTLS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package tls import ( @@ -8,7 +12,7 @@ import ( // Alternate certificate message formats (https://datatracker.ietf.org/doc/html/rfc7250) are not // supported. // https://datatracker.ietf.org/doc/html/rfc8879 -type compressedCertificateMsg struct { +type utlsCompressedCertificateMsg struct { raw []byte algorithm uint16 @@ -16,13 +20,13 @@ type compressedCertificateMsg struct { compressedCertificateMessage []byte } -func (m *compressedCertificateMsg) marshal() []byte { +func (m *utlsCompressedCertificateMsg) marshal() []byte { if m.raw != nil { return m.raw } var b cryptobyte.Builder - b.AddUint8(typeCompressedCertificate) + b.AddUint8(utlsTypeCompressedCertificate) b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { b.AddUint16(m.algorithm) b.AddUint24(m.uncompressedLength) @@ -35,8 +39,8 @@ func (m *compressedCertificateMsg) marshal() []byte { return m.raw } -func (m *compressedCertificateMsg) unmarshal(data []byte) bool { - *m = compressedCertificateMsg{raw: data} +func (m *utlsCompressedCertificateMsg) unmarshal(data []byte) bool { + *m = utlsCompressedCertificateMsg{raw: data} s := cryptobyte.String(data) if !s.Skip(4) || // message type and uint24 length field @@ -47,3 +51,83 @@ func (m *compressedCertificateMsg) unmarshal(data []byte) bool { } return true } + +type utlsEncryptedExtensionsMsgExtraFields struct { + hasApplicationSettings bool + applicationSettings []byte + customExtension []byte +} + +func (m *encryptedExtensionsMsg) utlsUnmarshal(extension uint16, extData cryptobyte.String) bool { + switch extension { + case utlsExtensionApplicationSettings: + m.utls.hasApplicationSettings = true + m.utls.applicationSettings = []byte(extData) + } + return true // success/unknown extension +} + +type utlsClientEncryptedExtensionsMsg struct { + raw []byte + applicationSettings []byte + hasApplicationSettings bool + customExtension []byte +} + +func (m *utlsClientEncryptedExtensionsMsg) marshal() (x []byte) { + if m.raw != nil { + return m.raw + } + + var builder cryptobyte.Builder + builder.AddUint8(typeEncryptedExtensions) + builder.AddUint24LengthPrefixed(func(body *cryptobyte.Builder) { + body.AddUint16LengthPrefixed(func(extensions *cryptobyte.Builder) { + if m.hasApplicationSettings { + extensions.AddUint16(utlsExtensionApplicationSettings) + extensions.AddUint16LengthPrefixed(func(msg *cryptobyte.Builder) { + msg.AddBytes(m.applicationSettings) + }) + } + if len(m.customExtension) > 0 { + extensions.AddUint16(utlsFakeExtensionCustom) + extensions.AddUint16LengthPrefixed(func(msg *cryptobyte.Builder) { + msg.AddBytes(m.customExtension) + }) + } + }) + }) + + m.raw = builder.BytesOrPanic() + return m.raw +} + +func (m *utlsClientEncryptedExtensionsMsg) unmarshal(data []byte) bool { + *m = utlsClientEncryptedExtensionsMsg{raw: data} + s := cryptobyte.String(data) + + var extensions cryptobyte.String + if !s.Skip(4) || // message type and uint24 length field + !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() { + return false + } + + for !extensions.Empty() { + var extension uint16 + var extData cryptobyte.String + if !extensions.ReadUint16(&extension) || + !extensions.ReadUint16LengthPrefixed(&extData) { + return false + } + + switch extension { + case utlsExtensionApplicationSettings: + m.hasApplicationSettings = true + m.applicationSettings = []byte(extData) + default: + // Unknown extensions are illegal in EncryptedExtensions. + return false + } + } + return true +} diff --git a/u_tls_extensions.go b/u_tls_extensions.go index c4816d3d..7cb73620 100644 --- a/u_tls_extensions.go +++ b/u_tls_extensions.go @@ -132,8 +132,8 @@ func (e *StatusRequestV2Extension) Read(b []byte) (int, error) { return 0, io.ErrShortBuffer } // RFC 4366, section 3.6 - b[0] = byte(17 >> 8) - b[1] = byte(17) + b[0] = byte(extensionStatusRequestV2 >> 8) + b[1] = byte(extensionStatusRequestV2) b[2] = 0 b[3] = 9 b[4] = 0 @@ -356,17 +356,9 @@ func (e *ALPNExtension) Read(b []byte) (int, error) { return e.Len(), io.EOF } -// ApplicationSettingsExtension represents the TLS ALPS extension. At the time -// of this writing, this extension is currently a draft: +// ApplicationSettingsExtension represents the TLS ALPS extension. +// At the time of this writing, this extension is currently a draft: // https://datatracker.ietf.org/doc/html/draft-vvv-tls-alps-01 -// -// This library does not offer actual support for ALPS. This extension is -// "faked" - it is advertised by the client, but not respected if the server -// responds with support. -// -// In the normal convention of this library, this type name would be prefixed -// with 'Fake'. The existing name is retained for backwards compatibility -// reasons. type ApplicationSettingsExtension struct { SupportedProtocols []string } @@ -389,8 +381,8 @@ func (e *ApplicationSettingsExtension) Read(b []byte) (int, error) { } // Read Type. - b[0] = byte(fakeExtensionALPS >> 8) // hex: 44 dec: 68 - b[1] = byte(fakeExtensionALPS & 0xff) // hex: 69 dec: 105 + b[0] = byte(utlsExtensionApplicationSettings >> 8) // hex: 44 dec: 68 + b[1] = byte(utlsExtensionApplicationSettings & 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) @@ -863,7 +855,7 @@ func (e *FakeChannelIDExtension) Read(b []byte) (int, error) { } extensionID := fakeExtensionChannelID if e.OldExtensionID { - extensionID = fakeExtensionChannelIDOld + extensionID = fakeOldExtensionChannelID } // https://tools.ietf.org/html/draft-balfanz-tls-channelid-00 b[0] = byte(extensionID >> 8)