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} {