Check duplicate extensions before processing.

ClientHello and ServerHello are not allowed to include duplicate extensions.
Add a new helper function to check this and call as appropriate. Remove ad-hoc
per-extension duplicate checks which are no unnecessary.

Add runner.go tests to verify such message correctly rejected.

Change-Id: I7babd5b642dfec941459512869e2dd6de26a831c
Reviewed-on: https://boringssl-review.googlesource.com/1100
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index fd78eb6..df7cacf 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -345,6 +345,10 @@
 	// FailIfNotFallbackSCSV causes a server handshake to fail if the
 	// client doesn't send the fallback SCSV value.
 	FailIfNotFallbackSCSV bool
+
+	// DuplicateExtension causes an extra empty extension of bogus type to
+	// be emitted in either the ClientHello or the ServerHello.
+	DuplicateExtension bool
 }
 
 func (c *Config) serverInit() {
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index f335d03..220e489 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -47,6 +47,7 @@
 		supportedPoints:     []uint8{pointFormatUncompressed},
 		nextProtoNeg:        len(c.config.NextProtos) > 0,
 		secureRenegotiation: true,
+		duplicateExtension:  c.config.Bugs.DuplicateExtension,
 	}
 
 	possibleCipherSuites := c.config.cipherSuites()
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go
index e31f47b..edb45d8 100644
--- a/ssl/test/runner/handshake_messages.go
+++ b/ssl/test/runner/handshake_messages.go
@@ -22,6 +22,7 @@
 	sessionTicket       []uint8
 	signatureAndHashes  []signatureAndHash
 	secureRenegotiation bool
+	duplicateExtension  bool
 }
 
 func (m *clientHelloMsg) equal(i interface{}) bool {
@@ -86,6 +87,9 @@
 		extensionsLength += 1
 		numExtensions++
 	}
+	if m.duplicateExtension {
+		numExtensions += 2
+	}
 	if numExtensions > 0 {
 		extensionsLength += 4 * numExtensions
 		length += 2 + extensionsLength
@@ -118,6 +122,12 @@
 		z[1] = byte(extensionsLength)
 		z = z[2:]
 	}
+	if m.duplicateExtension {
+		// Add a duplicate bogus extension at the beginning and end.
+		z[0] = 0xff
+		z[1] = 0xff
+		z = z[4:]
+	}
 	if m.nextProtoNeg {
 		z[0] = byte(extensionNextProtoNeg >> 8)
 		z[1] = byte(extensionNextProtoNeg & 0xff)
@@ -237,6 +247,12 @@
 		z[3] = 1
 		z = z[5:]
 	}
+	if m.duplicateExtension {
+		// Add a duplicate bogus extension at the beginning and end.
+		z[0] = 0xff
+		z[1] = 0xff
+		z = z[4:]
+	}
 
 	m.raw = x
 
@@ -419,6 +435,7 @@
 	ocspStapling        bool
 	ticketSupported     bool
 	secureRenegotiation bool
+	duplicateExtension  bool
 }
 
 func (m *serverHelloMsg) equal(i interface{}) bool {
@@ -468,6 +485,9 @@
 		extensionsLength += 1
 		numExtensions++
 	}
+	if m.duplicateExtension {
+		numExtensions += 2
+	}
 	if numExtensions > 0 {
 		extensionsLength += 4 * numExtensions
 		length += 2 + extensionsLength
@@ -494,6 +514,12 @@
 		z[1] = byte(extensionsLength)
 		z = z[2:]
 	}
+	if m.duplicateExtension {
+		// Add a duplicate bogus extension at the beginning and end.
+		z[0] = 0xff
+		z[1] = 0xff
+		z = z[4:]
+	}
 	if m.nextProtoNeg {
 		z[0] = byte(extensionNextProtoNeg >> 8)
 		z[1] = byte(extensionNextProtoNeg & 0xff)
@@ -528,6 +554,12 @@
 		z[3] = 1
 		z = z[5:]
 	}
+	if m.duplicateExtension {
+		// Add a duplicate bogus extension at the beginning and end.
+		z[0] = 0xff
+		z[1] = 0xff
+		z = z[4:]
+	}
 
 	m.raw = x
 
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 854c7ff..328c15f 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -157,6 +157,7 @@
 	}
 	hs.hello.secureRenegotiation = hs.clientHello.secureRenegotiation
 	hs.hello.compressionMethod = compressionNone
+	hs.hello.duplicateExtension = c.config.Bugs.DuplicateExtension
 	if len(hs.clientHello.serverName) > 0 {
 		c.serverName = hs.clientHello.serverName
 	}
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index f253f89..96b52fa 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -137,12 +137,34 @@
 	},
 	{
 		testType: serverTest,
-		name: "ServerNameExtension",
+		name:     "ServerNameExtension",
 		config: Config{
 			ServerName: "example.com",
 		},
 		flags: []string{"-expect-server-name", "example.com"},
 	},
+	{
+		testType: clientTest,
+		name:     "DuplicateExtensionClient",
+		config: Config{
+			Bugs: ProtocolBugs{
+				DuplicateExtension: true,
+			},
+		},
+		shouldFail:         true,
+		expectedLocalError: "remote error: error decoding message",
+	},
+	{
+		testType: serverTest,
+		name:     "DuplicateExtensionServer",
+		config: Config{
+			Bugs: ProtocolBugs{
+				DuplicateExtension: true,
+			},
+		},
+		shouldFail:         true,
+		expectedLocalError: "remote error: error decoding message",
+	},
 }
 
 func doExchange(tlsConn *Conn, messageLen int) error {