|
|
@@ -7,6 +7,13 @@ package tls
|
|
|
import "bytes"
|
|
|
|
|
|
type clientHelloMsg struct {
|
|
|
+ // [Psiphon]
|
|
|
+ // emulateChrome indicates whether to use Chrome/BoringSSL-like
|
|
|
+ // extension order. This order is used only when emulateChrome
|
|
|
+ // is set to ensure the automated tests run against pre-recorded
|
|
|
+ // "testdata".
|
|
|
+ emulateChrome bool
|
|
|
+
|
|
|
raw []byte
|
|
|
vers uint16
|
|
|
random []byte
|
|
|
@@ -25,6 +32,12 @@ type clientHelloMsg struct {
|
|
|
secureRenegotiation []byte
|
|
|
secureRenegotiationSupported bool
|
|
|
alpnProtocols []string
|
|
|
+
|
|
|
+ // [Psiphon]
|
|
|
+ // Additional extensions required for EmulateChrome.
|
|
|
+ // Note: omitted from clientHelloMsg.equal()
|
|
|
+ extendedMasterSecretSupported bool
|
|
|
+ channelIDSupported bool
|
|
|
}
|
|
|
|
|
|
func (m *clientHelloMsg) equal(i interface{}) bool {
|
|
|
@@ -106,6 +119,16 @@ func (m *clientHelloMsg) marshal() []byte {
|
|
|
if m.scts {
|
|
|
numExtensions++
|
|
|
}
|
|
|
+
|
|
|
+ // [Psiphon]
|
|
|
+ // Additional extensions required for EmulateChrome.
|
|
|
+ if m.extendedMasterSecretSupported {
|
|
|
+ numExtensions++
|
|
|
+ }
|
|
|
+ if m.channelIDSupported {
|
|
|
+ numExtensions++
|
|
|
+ }
|
|
|
+
|
|
|
if numExtensions > 0 {
|
|
|
extensionsLength += 4 * numExtensions
|
|
|
length += 2 + extensionsLength
|
|
|
@@ -132,19 +155,17 @@ func (m *clientHelloMsg) marshal() []byte {
|
|
|
z[0] = uint8(len(m.compressionMethods))
|
|
|
copy(z[1:], m.compressionMethods)
|
|
|
|
|
|
- z = z[1+len(m.compressionMethods):]
|
|
|
- if numExtensions > 0 {
|
|
|
- z[0] = byte(extensionsLength >> 8)
|
|
|
- z[1] = byte(extensionsLength)
|
|
|
- z = z[2:]
|
|
|
- }
|
|
|
- if m.nextProtoNeg {
|
|
|
+ // [Psiphon]
|
|
|
+ // The extension marshal order changes as required for EmulateChrome.
|
|
|
+
|
|
|
+ marshalNextProtoNeg := func() {
|
|
|
z[0] = byte(extensionNextProtoNeg >> 8)
|
|
|
z[1] = byte(extensionNextProtoNeg & 0xff)
|
|
|
// The length is always 0
|
|
|
z = z[4:]
|
|
|
}
|
|
|
- if len(m.serverName) > 0 {
|
|
|
+
|
|
|
+ marshalServerName := func() {
|
|
|
z[0] = byte(extensionServerName >> 8)
|
|
|
z[1] = byte(extensionServerName & 0xff)
|
|
|
l := len(m.serverName) + 5
|
|
|
@@ -178,7 +199,8 @@ func (m *clientHelloMsg) marshal() []byte {
|
|
|
copy(z[5:], []byte(m.serverName))
|
|
|
z = z[l:]
|
|
|
}
|
|
|
- if m.ocspStapling {
|
|
|
+
|
|
|
+ marshalStatusRequest := func() {
|
|
|
// RFC 4366, section 3.6
|
|
|
z[0] = byte(extensionStatusRequest >> 8)
|
|
|
z[1] = byte(extensionStatusRequest)
|
|
|
@@ -188,7 +210,8 @@ func (m *clientHelloMsg) marshal() []byte {
|
|
|
// Two zero valued uint16s for the two lengths.
|
|
|
z = z[9:]
|
|
|
}
|
|
|
- if len(m.supportedCurves) > 0 {
|
|
|
+
|
|
|
+ marshalSupportedCurves := func() {
|
|
|
// http://tools.ietf.org/html/rfc4492#section-5.5.1
|
|
|
z[0] = byte(extensionSupportedCurves >> 8)
|
|
|
z[1] = byte(extensionSupportedCurves)
|
|
|
@@ -205,7 +228,8 @@ func (m *clientHelloMsg) marshal() []byte {
|
|
|
z = z[2:]
|
|
|
}
|
|
|
}
|
|
|
- if len(m.supportedPoints) > 0 {
|
|
|
+
|
|
|
+ marshalSupportedPoints := func() {
|
|
|
// http://tools.ietf.org/html/rfc4492#section-5.5.2
|
|
|
z[0] = byte(extensionSupportedPoints >> 8)
|
|
|
z[1] = byte(extensionSupportedPoints)
|
|
|
@@ -220,7 +244,8 @@ func (m *clientHelloMsg) marshal() []byte {
|
|
|
z = z[1:]
|
|
|
}
|
|
|
}
|
|
|
- if m.ticketSupported {
|
|
|
+
|
|
|
+ marshalSessionTicket := func() {
|
|
|
// http://tools.ietf.org/html/rfc5077#section-3.2
|
|
|
z[0] = byte(extensionSessionTicket >> 8)
|
|
|
z[1] = byte(extensionSessionTicket)
|
|
|
@@ -231,7 +256,8 @@ func (m *clientHelloMsg) marshal() []byte {
|
|
|
copy(z, m.sessionTicket)
|
|
|
z = z[len(m.sessionTicket):]
|
|
|
}
|
|
|
- if len(m.signatureAndHashes) > 0 {
|
|
|
+
|
|
|
+ marshalSignatureAlgorithms := func() {
|
|
|
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
|
|
|
z[0] = byte(extensionSignatureAlgorithms >> 8)
|
|
|
z[1] = byte(extensionSignatureAlgorithms)
|
|
|
@@ -250,7 +276,8 @@ func (m *clientHelloMsg) marshal() []byte {
|
|
|
z = z[2:]
|
|
|
}
|
|
|
}
|
|
|
- if m.secureRenegotiationSupported {
|
|
|
+
|
|
|
+ marshalRenegotiationInfo := func() {
|
|
|
z[0] = byte(extensionRenegotiationInfo >> 8)
|
|
|
z[1] = byte(extensionRenegotiationInfo & 0xff)
|
|
|
z[2] = 0
|
|
|
@@ -260,7 +287,8 @@ func (m *clientHelloMsg) marshal() []byte {
|
|
|
copy(z, m.secureRenegotiation)
|
|
|
z = z[len(m.secureRenegotiation):]
|
|
|
}
|
|
|
- if len(m.alpnProtocols) > 0 {
|
|
|
+
|
|
|
+ marshalALPN := func() {
|
|
|
z[0] = byte(extensionALPN >> 8)
|
|
|
z[1] = byte(extensionALPN & 0xff)
|
|
|
lengths := z[2:]
|
|
|
@@ -281,7 +309,8 @@ func (m *clientHelloMsg) marshal() []byte {
|
|
|
lengths[0] = byte(stringsLength >> 8)
|
|
|
lengths[1] = byte(stringsLength)
|
|
|
}
|
|
|
- if m.scts {
|
|
|
+
|
|
|
+ marshalSCT := func() {
|
|
|
// https://tools.ietf.org/html/rfc6962#section-3.3.1
|
|
|
z[0] = byte(extensionSCT >> 8)
|
|
|
z[1] = byte(extensionSCT)
|
|
|
@@ -289,6 +318,106 @@ func (m *clientHelloMsg) marshal() []byte {
|
|
|
z = z[4:]
|
|
|
}
|
|
|
|
|
|
+ // [Psiphon]
|
|
|
+ // Additional extensions required for EmulateChrome.
|
|
|
+ marshalExtendedMasterSecret := func() {
|
|
|
+ // https://tools.ietf.org/html/draft-ietf-tls-session-hash-01
|
|
|
+ z[0] = byte(extensionExtendedMasterSecret >> 8)
|
|
|
+ z[1] = byte(extensionExtendedMasterSecret & 0xff)
|
|
|
+ z = z[4:]
|
|
|
+ }
|
|
|
+ marshalChannelID := func() {
|
|
|
+ if m.channelIDSupported {
|
|
|
+ z[0] = byte(extensionChannelID >> 8)
|
|
|
+ z[1] = byte(extensionChannelID & 0xff)
|
|
|
+ z = z[4:]
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ z = z[1+len(m.compressionMethods):]
|
|
|
+ if numExtensions > 0 {
|
|
|
+ z[0] = byte(extensionsLength >> 8)
|
|
|
+ z[1] = byte(extensionsLength)
|
|
|
+ z = z[2:]
|
|
|
+ }
|
|
|
+
|
|
|
+ if m.emulateChrome {
|
|
|
+
|
|
|
+ // [Psiphon]
|
|
|
+ // This code handles extension ordering only; configuration
|
|
|
+ // of extensions as required for EmulateChrome is handled
|
|
|
+ // in Conn.clientHandshae().
|
|
|
+
|
|
|
+ if m.secureRenegotiationSupported {
|
|
|
+ marshalRenegotiationInfo()
|
|
|
+ }
|
|
|
+ if len(m.serverName) > 0 {
|
|
|
+ marshalServerName()
|
|
|
+ }
|
|
|
+ if m.extendedMasterSecretSupported {
|
|
|
+ marshalExtendedMasterSecret()
|
|
|
+ }
|
|
|
+ if m.ticketSupported {
|
|
|
+ marshalSessionTicket()
|
|
|
+ }
|
|
|
+ if len(m.signatureAndHashes) > 0 {
|
|
|
+ marshalSignatureAlgorithms()
|
|
|
+ }
|
|
|
+ if m.ocspStapling {
|
|
|
+ marshalStatusRequest()
|
|
|
+ }
|
|
|
+ if m.scts {
|
|
|
+ marshalSCT()
|
|
|
+ }
|
|
|
+ if m.nextProtoNeg {
|
|
|
+ marshalNextProtoNeg()
|
|
|
+ }
|
|
|
+ if len(m.alpnProtocols) > 0 {
|
|
|
+ marshalALPN()
|
|
|
+ }
|
|
|
+ if m.channelIDSupported {
|
|
|
+ marshalChannelID()
|
|
|
+ }
|
|
|
+ if len(m.supportedPoints) > 0 {
|
|
|
+ marshalSupportedPoints()
|
|
|
+ }
|
|
|
+ if len(m.supportedCurves) > 0 {
|
|
|
+ marshalSupportedCurves()
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ if m.nextProtoNeg {
|
|
|
+ marshalNextProtoNeg()
|
|
|
+ }
|
|
|
+ if len(m.serverName) > 0 {
|
|
|
+ marshalServerName()
|
|
|
+ }
|
|
|
+ if m.ocspStapling {
|
|
|
+ marshalStatusRequest()
|
|
|
+ }
|
|
|
+ if len(m.supportedCurves) > 0 {
|
|
|
+ marshalSupportedCurves()
|
|
|
+ }
|
|
|
+ if len(m.supportedPoints) > 0 {
|
|
|
+ marshalSupportedPoints()
|
|
|
+ }
|
|
|
+ if m.ticketSupported {
|
|
|
+ marshalSessionTicket()
|
|
|
+ }
|
|
|
+ if len(m.signatureAndHashes) > 0 {
|
|
|
+ marshalSignatureAlgorithms()
|
|
|
+ }
|
|
|
+ if m.secureRenegotiationSupported {
|
|
|
+ marshalRenegotiationInfo()
|
|
|
+ }
|
|
|
+ if len(m.alpnProtocols) > 0 {
|
|
|
+ marshalALPN()
|
|
|
+ }
|
|
|
+ if m.scts {
|
|
|
+ marshalSCT()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
m.raw = x
|
|
|
|
|
|
return x
|