Implement custom extensions.

This change mirrors upstream's custom extension API because we have some
internal users that depend on it.

Change-Id: I408e442de0a55df7b05c872c953ff048cd406513
Reviewed-on: https://boringssl-review.googlesource.com/5471
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index fb78ef1..7b7a35b 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -82,6 +82,7 @@
 	extensionSignedCertificateTimestamp uint16 = 18
 	extensionExtendedMasterSecret       uint16 = 23
 	extensionSessionTicket              uint16 = 35
+	extensionCustom                     uint16 = 1234 // not IANA assigned
 	extensionNextProtoNeg               uint16 = 13172 // not IANA assigned
 	extensionRenegotiationInfo          uint16 = 0xff01
 	extensionChannelID                  uint16 = 30032 // not IANA assigned
@@ -736,6 +737,14 @@
 	// RequireClientHelloSize, if not zero, is the required length in bytes
 	// of the ClientHello /record/. This is checked by the server.
 	RequireClientHelloSize int
+
+	// CustomExtension, if not empty, contains the contents of an extension
+	// that will be added to client/server hellos.
+	CustomExtension string
+
+	// ExpectedCustomExtension, if not nil, contains the expected contents
+	// of a custom extension.
+	ExpectedCustomExtension *string
 }
 
 func (c *Config) serverInit() {
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index bc10fe7..a96cd9c 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -73,6 +73,7 @@
 		extendedMasterSecret:    c.config.maxVersion() >= VersionTLS10,
 		srtpProtectionProfiles:  c.config.SRTPProtectionProfiles,
 		srtpMasterKeyIdentifier: c.config.Bugs.SRTPMasterKeyIdentifer,
+		customExtension:         c.config.Bugs.CustomExtension,
 	}
 
 	if c.config.Bugs.SendClientVersion != 0 {
@@ -290,6 +291,12 @@
 		}
 	}
 
+	if expected := c.config.Bugs.ExpectedCustomExtension; expected != nil {
+		if serverHello.customExtension != *expected {
+			return fmt.Errorf("tls: bad custom extension contents %q", serverHello.customExtension)
+		}
+	}
+
 	hs := &clientHandshakeState{
 		c:            c,
 		serverHello:  serverHello,
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go
index 46ff2fd..92f603a 100644
--- a/ssl/test/runner/handshake_messages.go
+++ b/ssl/test/runner/handshake_messages.go
@@ -32,6 +32,7 @@
 	srtpProtectionProfiles  []uint16
 	srtpMasterKeyIdentifier string
 	sctListSupported        bool
+	customExtension         string
 }
 
 func (m *clientHelloMsg) equal(i interface{}) bool {
@@ -65,7 +66,8 @@
 		m.extendedMasterSecret == m1.extendedMasterSecret &&
 		eqUint16s(m.srtpProtectionProfiles, m1.srtpProtectionProfiles) &&
 		m.srtpMasterKeyIdentifier == m1.srtpMasterKeyIdentifier &&
-		m.sctListSupported == m1.sctListSupported
+		m.sctListSupported == m1.sctListSupported &&
+		m.customExtension == m1.customExtension
 }
 
 func (m *clientHelloMsg) marshal() []byte {
@@ -138,6 +140,10 @@
 	if m.sctListSupported {
 		numExtensions++
 	}
+	if l := len(m.customExtension); l > 0 {
+		extensionsLength += l
+		numExtensions++
+	}
 	if numExtensions > 0 {
 		extensionsLength += 4 * numExtensions
 		length += 2 + extensionsLength
@@ -376,6 +382,14 @@
 		z[1] = byte(extensionSignedCertificateTimestamp & 0xff)
 		z = z[4:]
 	}
+	if l := len(m.customExtension); l > 0 {
+		z[0] = byte(extensionCustom >> 8)
+		z[1] = byte(extensionCustom & 0xff)
+		z[2] = byte(l >> 8)
+		z[3] = byte(l & 0xff)
+		copy(z[4:], []byte(m.customExtension))
+		z = z[4 + l:]
+	}
 
 	m.raw = x
 
@@ -443,6 +457,7 @@
 	m.signatureAndHashes = nil
 	m.alpnProtocols = nil
 	m.extendedMasterSecret = false
+	m.customExtension = ""
 
 	if len(data) == 0 {
 		// ClientHello is optionally followed by extension data
@@ -604,6 +619,8 @@
 				return false
 			}
 			m.sctListSupported = true
+		case extensionCustom:
+			m.customExtension = string(data[:length])
 		}
 		data = data[length:]
 	}
@@ -632,6 +649,7 @@
 	srtpProtectionProfile   uint16
 	srtpMasterKeyIdentifier string
 	sctList                 []byte
+	customExtension         string
 }
 
 func (m *serverHelloMsg) marshal() []byte {
@@ -686,6 +704,10 @@
 		extensionsLength += len(m.sctList)
 		numExtensions++
 	}
+	if l := len(m.customExtension); l > 0 {
+		extensionsLength += l
+		numExtensions++
+	}
 
 	if numExtensions > 0 {
 		extensionsLength += 4 * numExtensions
@@ -811,6 +833,14 @@
 		copy(z[4:], m.sctList)
 		z = z[4+l:]
 	}
+	if l := len(m.customExtension); l > 0 {
+		z[0] = byte(extensionCustom >> 8)
+		z[1] = byte(extensionCustom & 0xff)
+		z[2] = byte(l >> 8)
+		z[3] = byte(l & 0xff)
+		copy(z[4:], []byte(m.customExtension))
+		z = z[4 + l:]
+	}
 
 	m.raw = x
 
@@ -844,6 +874,7 @@
 	m.alpnProtocol = ""
 	m.alpnProtocolEmpty = false
 	m.extendedMasterSecret = false
+	m.customExtension = ""
 
 	if len(data) == 0 {
 		// ServerHello is optionally followed by extension data
@@ -948,6 +979,8 @@
 				return false
 			}
 			m.sctList = data[2:length]
+		case extensionCustom:
+			m.customExtension = string(data[:length])
 		}
 		data = data[length:]
 	}
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 5d37674..7686402 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -210,8 +210,10 @@
 	}
 	c.haveVers = true
 
-	hs.hello = new(serverHelloMsg)
-	hs.hello.isDTLS = c.isDTLS
+	hs.hello = &serverHelloMsg {
+		isDTLS: c.isDTLS,
+		customExtension: config.Bugs.CustomExtension,
+	}
 
 	supportedCurve := false
 	preferredCurves := config.curvePreferences()
@@ -340,6 +342,12 @@
 		hs.hello.srtpProtectionProfile = c.config.Bugs.SendSRTPProtectionProfile
 	}
 
+	if expected := c.config.Bugs.ExpectedCustomExtension; expected != nil {
+		if hs.clientHello.customExtension != *expected {
+			return false, fmt.Errorf("tls: bad custom extension contents %q", hs.clientHello.customExtension)
+		}
+	}
+
 	_, hs.ecdsaOk = hs.cert.PrivateKey.(*ecdsa.PrivateKey)
 
 	// For test purposes, check that the peer never offers a session when
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index ff10c05..d66dc74 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -3627,6 +3627,97 @@
 	}
 }
 
+func addCustomExtensionTests() {
+	expectedContents := "custom extension"
+	emptyString := ""
+
+	for _, isClient := range []bool{false, true} {
+		suffix := "Server"
+		flag := "-enable-server-custom-extension"
+		testType := serverTest
+		if isClient {
+			suffix = "Client"
+			flag = "-enable-client-custom-extension"
+			testType = clientTest
+		}
+
+		testCases = append(testCases, testCase{
+			testType: testType,
+			name: "CustomExtensions-" + suffix,
+			config: Config{
+				Bugs: ProtocolBugs {
+					CustomExtension: expectedContents,
+					ExpectedCustomExtension: &expectedContents,
+				},
+			},
+			flags: []string{flag},
+		})
+
+		// If the parse callback fails, the handshake should also fail.
+		testCases = append(testCases, testCase{
+			testType: testType,
+			name: "CustomExtensions-ParseError-" + suffix,
+			config: Config{
+				Bugs: ProtocolBugs {
+					CustomExtension: expectedContents + "foo",
+					ExpectedCustomExtension: &expectedContents,
+				},
+			},
+			flags: []string{flag},
+			shouldFail: true,
+			expectedError: ":CUSTOM_EXTENSION_ERROR:",
+		})
+
+		// If the add callback fails, the handshake should also fail.
+		testCases = append(testCases, testCase{
+			testType: testType,
+			name: "CustomExtensions-FailAdd-" + suffix,
+			config: Config{
+				Bugs: ProtocolBugs {
+					CustomExtension: expectedContents,
+					ExpectedCustomExtension: &expectedContents,
+				},
+			},
+			flags: []string{flag, "-custom-extension-fail-add"},
+			shouldFail: true,
+			expectedError: ":CUSTOM_EXTENSION_ERROR:",
+		})
+
+		// If the add callback returns zero, no extension should be
+		// added.
+		skipCustomExtension := expectedContents
+		if isClient {
+			// For the case where the client skips sending the
+			// custom extension, the server must not “echo” it.
+			skipCustomExtension = ""
+		}
+		testCases = append(testCases, testCase{
+			testType: testType,
+			name: "CustomExtensions-Skip-" + suffix,
+			config: Config{
+				Bugs: ProtocolBugs {
+					CustomExtension: skipCustomExtension,
+					ExpectedCustomExtension: &emptyString,
+				},
+			},
+			flags: []string{flag, "-custom-extension-skip"},
+		})
+	}
+
+	// The custom extension add callback should not be called if the client
+	// doesn't send the extension.
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name: "CustomExtensions-NotCalled-Server",
+		config: Config{
+			Bugs: ProtocolBugs {
+				ExpectedCustomExtension: &emptyString,
+			},
+		},
+		flags: []string{"-enable-server-custom-extension", "-custom-extension-fail-add"},
+	})
+}
+
 func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) {
 	defer wg.Done()
 
@@ -3723,6 +3814,7 @@
 	addDTLSRetransmitTests()
 	addExportKeyingMaterialTests()
 	addTLSUniqueTests()
+	addCustomExtensionTests()
 	for _, async := range []bool{false, true} {
 		for _, splitHandshake := range []bool{false, true} {
 			for _, protocol := range []protocol{tls, dtls} {