| // Copyright 2025 The BoringSSL Authors | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //     https://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | package runner | 
 |  | 
 | import "slices" | 
 |  | 
 | func addKeyUpdateTests() { | 
 | 	// TLS tests. | 
 | 	testCases = append(testCases, testCase{ | 
 | 		name: "KeyUpdate-ToClient", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 		}, | 
 | 		sendKeyUpdates:   10, | 
 | 		keyUpdateRequest: keyUpdateNotRequested, | 
 | 	}) | 
 | 	testCases = append(testCases, testCase{ | 
 | 		testType: serverTest, | 
 | 		name:     "KeyUpdate-ToServer", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 		}, | 
 | 		sendKeyUpdates:   10, | 
 | 		keyUpdateRequest: keyUpdateNotRequested, | 
 | 	}) | 
 | 	testCases = append(testCases, testCase{ | 
 | 		name: "KeyUpdate-FromClient", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 		}, | 
 | 		expectUnsolicitedKeyUpdate: true, | 
 | 		flags:                      []string{"-key-update"}, | 
 | 	}) | 
 | 	testCases = append(testCases, testCase{ | 
 | 		testType: serverTest, | 
 | 		name:     "KeyUpdate-FromServer", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 		}, | 
 | 		expectUnsolicitedKeyUpdate: true, | 
 | 		flags:                      []string{"-key-update"}, | 
 | 	}) | 
 | 	testCases = append(testCases, testCase{ | 
 | 		name: "KeyUpdate-InvalidRequestMode", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 		}, | 
 | 		sendKeyUpdates:   1, | 
 | 		keyUpdateRequest: 42, | 
 | 		shouldFail:       true, | 
 | 		expectedError:    ":DECODE_ERROR:", | 
 | 	}) | 
 | 	testCases = append(testCases, testCase{ | 
 | 		// Test that shim responds to KeyUpdate requests. | 
 | 		name: "KeyUpdate-Requested", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 			Bugs: ProtocolBugs{ | 
 | 				RejectUnsolicitedKeyUpdate: true, | 
 | 			}, | 
 | 		}, | 
 | 		// Test the shim receiving many KeyUpdates in a row. | 
 | 		sendKeyUpdates:   5, | 
 | 		messageCount:     5, | 
 | 		keyUpdateRequest: keyUpdateRequested, | 
 | 	}) | 
 | 	testCases = append(testCases, testCase{ | 
 | 		// Test that shim responds to KeyUpdate requests if peer's KeyUpdate is | 
 | 		// discovered while a write is pending. | 
 | 		name: "KeyUpdate-Requested-UnfinishedWrite", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 			Bugs: ProtocolBugs{ | 
 | 				RejectUnsolicitedKeyUpdate: true, | 
 | 			}, | 
 | 		}, | 
 | 		// Test the shim receiving many KeyUpdates in a row. | 
 | 		sendKeyUpdates:          5, | 
 | 		messageCount:            5, | 
 | 		keyUpdateRequest:        keyUpdateRequested, | 
 | 		readWithUnfinishedWrite: true, | 
 | 		flags:                   []string{"-async"}, | 
 | 	}) | 
 |  | 
 | 	// DTLS tests. | 
 | 	testCases = append(testCases, testCase{ | 
 | 		protocol: dtls, | 
 | 		name:     "KeyUpdate-ToClient-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 		}, | 
 | 		// Send many KeyUpdates to make sure record reassembly can handle it. | 
 | 		sendKeyUpdates:   10, | 
 | 		keyUpdateRequest: keyUpdateNotRequested, | 
 | 	}) | 
 | 	testCases = append(testCases, testCase{ | 
 | 		protocol: dtls, | 
 | 		testType: serverTest, | 
 | 		name:     "KeyUpdate-ToServer-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 		}, | 
 | 		sendKeyUpdates:   10, | 
 | 		keyUpdateRequest: keyUpdateNotRequested, | 
 | 	}) | 
 |  | 
 | 	// Test that the shim accounts for packet loss when processing KeyUpdate. | 
 | 	testCases = append(testCases, testCase{ | 
 | 		protocol: dtls, | 
 | 		name:     "KeyUpdate-ToClient-PacketLoss-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 			Bugs: ProtocolBugs{ | 
 | 				WriteFlightDTLS: func(c *DTLSController, prev, received, next []DTLSMessage, records []DTLSRecordNumberInfo) { | 
 | 					if next[0].Type != typeKeyUpdate { | 
 | 						c.WriteFlight(next) | 
 | 						return | 
 | 					} | 
 |  | 
 | 					// Send the KeyUpdate. The shim should ACK it. | 
 | 					c.WriteFlight(next) | 
 | 					ackTimeout := timeouts[0] / 4 | 
 | 					c.AdvanceClock(ackTimeout) | 
 | 					c.ReadACK(c.InEpoch()) | 
 |  | 
 | 					// The shim should continue reading data at the old epoch. | 
 | 					// The ACK may not have come through. | 
 | 					msg := []byte("test") | 
 | 					c.WriteAppData(c.OutEpoch()-1, msg) | 
 | 					c.ReadAppData(c.InEpoch(), expectedReply(msg)) | 
 |  | 
 | 					// Re-send KeyUpdate. The shim should ACK it again. The ACK | 
 | 					// may not have come through. | 
 | 					c.WriteFlight(next) | 
 | 					c.AdvanceClock(ackTimeout) | 
 | 					c.ReadACK(c.InEpoch()) | 
 |  | 
 | 					// The shim should be able to read data at the new epoch. | 
 | 					c.WriteAppData(c.OutEpoch(), msg) | 
 | 					c.ReadAppData(c.InEpoch(), expectedReply(msg)) | 
 |  | 
 | 					// The shim continues to accept application data at the old | 
 | 					// epoch, for a period of time. | 
 | 					c.WriteAppData(c.OutEpoch()-1, msg) | 
 | 					c.ReadAppData(c.InEpoch(), expectedReply(msg)) | 
 |  | 
 | 					// It will even ACK the retransmission, though it knows the | 
 | 					// shim has seen the ACK. | 
 | 					c.WriteFlight(next) | 
 | 					c.AdvanceClock(ackTimeout) | 
 | 					c.ReadACK(c.InEpoch()) | 
 |  | 
 | 					// After some time has passed, the shim will discard the old | 
 | 					// epoch. The following writes should be ignored. | 
 | 					c.AdvanceClock(dtlsPrevEpochExpiration) | 
 | 					f := next[0].Fragment(0, len(next[0].Data)) | 
 | 					f.ShouldDiscard = true | 
 | 					c.WriteFragments([]DTLSFragment{f}) | 
 | 					c.WriteAppData(c.OutEpoch()-1, msg) | 
 | 				}, | 
 | 			}, | 
 | 		}, | 
 | 		sendKeyUpdates:   10, | 
 | 		keyUpdateRequest: keyUpdateNotRequested, | 
 | 		flags:            []string{"-async"}, | 
 | 	}) | 
 |  | 
 | 	// In DTLS, we KeyUpdate before read, rather than write, because the | 
 | 	// KeyUpdate will not be applied before the shim reads the ACK. | 
 | 	testCases = append(testCases, testCase{ | 
 | 		protocol: dtls, | 
 | 		name:     "KeyUpdate-FromClient-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 		}, | 
 | 		shimSendsKeyUpdateBeforeRead: true, | 
 | 		// Perform several message exchanges to update keys several times. | 
 | 		messageCount: 10, | 
 | 	}) | 
 | 	testCases = append(testCases, testCase{ | 
 | 		protocol: dtls, | 
 | 		testType: serverTest, | 
 | 		name:     "KeyUpdate-FromServer-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 		}, | 
 | 		shimSendsKeyUpdateBeforeRead: true, | 
 | 		// Perform several message exchanges to update keys several times. | 
 | 		messageCount: 10, | 
 | 		// Avoid NewSessionTicket messages getting in the way of ReadKeyUpdate. | 
 | 		flags: []string{"-no-ticket"}, | 
 | 	}) | 
 |  | 
 | 	// If the shim has a pending unACKed flight, it defers sending KeyUpdate. | 
 | 	// BoringSSL does not support multiple outgoing flights at once. | 
 | 	testCases = append(testCases, testCase{ | 
 | 		protocol: dtls, | 
 | 		name:     "KeyUpdate-DeferredSend-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 			// Request a client certificate, so the shim has more to send. | 
 | 			ClientAuth: RequireAnyClientCert, | 
 | 			Bugs: ProtocolBugs{ | 
 | 				MaxPacketLength: 512, | 
 | 				ACKFlightDTLS: func(c *DTLSController, prev, received []DTLSMessage, records []DTLSRecordNumberInfo) { | 
 | 					if received[len(received)-1].Type != typeFinished { | 
 | 						c.WriteACK(c.OutEpoch(), records) | 
 | 						return | 
 | 					} | 
 |  | 
 | 					// This test relies on the Finished flight being multiple | 
 | 					// records. | 
 | 					if len(records) <= 1 { | 
 | 						panic("shim sent Finished flight in one record") | 
 | 					} | 
 |  | 
 | 					// Before ACKing Finished, do some rounds of exchanging | 
 | 					// application data. Although the shim has already scheduled | 
 | 					// KeyUpdate, it should not send the KeyUpdate until it gets | 
 | 					// an ACK. (If it sent KeyUpdate, ReadAppData would report | 
 | 					// an unexpected record.) | 
 | 					msg := []byte("test") | 
 | 					for i := 0; i < 10; i++ { | 
 | 						c.WriteAppData(c.OutEpoch(), msg) | 
 | 						c.ReadAppData(c.InEpoch(), expectedReply(msg)) | 
 | 					} | 
 |  | 
 | 					// ACK some of the Finished flight, but not all of it. | 
 | 					c.WriteACK(c.OutEpoch(), records[:1]) | 
 |  | 
 | 					// The shim continues to defer KeyUpdate. | 
 | 					for i := 0; i < 10; i++ { | 
 | 						c.WriteAppData(c.OutEpoch(), msg) | 
 | 						c.ReadAppData(c.InEpoch(), expectedReply(msg)) | 
 | 					} | 
 |  | 
 | 					// ACK the remainder. | 
 | 					c.WriteACK(c.OutEpoch(), records[1:]) | 
 |  | 
 | 					// The shim should now send KeyUpdate. Return to the test | 
 | 					// harness, which will look for it. | 
 | 				}, | 
 | 			}, | 
 | 		}, | 
 | 		shimCertificate:              &rsaChainCertificate, | 
 | 		shimSendsKeyUpdateBeforeRead: true, | 
 | 		flags:                        []string{"-mtu", "512"}, | 
 | 	}) | 
 |  | 
 | 	// The shim should not switch keys until it receives an ACK. | 
 | 	testCases = append(testCases, testCase{ | 
 | 		protocol: dtls, | 
 | 		name:     "KeyUpdate-WaitForACK-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 			Bugs: ProtocolBugs{ | 
 | 				MaxPacketLength: 512, | 
 | 				ACKFlightDTLS: func(c *DTLSController, prev, received []DTLSMessage, records []DTLSRecordNumberInfo) { | 
 | 					if received[0].Type != typeKeyUpdate { | 
 | 						c.WriteACK(c.OutEpoch(), records) | 
 | 						return | 
 | 					} | 
 |  | 
 | 					// Make the shim send application data. We have not yet | 
 | 					// ACKed KeyUpdate, so the shim should send at the previous | 
 | 					// epoch. Through each of these rounds, the shim will also | 
 | 					// try to KeyUpdate again. These calls will be suppressed | 
 | 					// because there is still an outstanding KeyUpdate. | 
 | 					msg := []byte("test") | 
 | 					for i := 0; i < 10; i++ { | 
 | 						c.WriteAppData(c.OutEpoch(), msg) | 
 | 						c.ReadAppData(c.InEpoch()-1, expectedReply(msg)) | 
 | 					} | 
 |  | 
 | 					// ACK the KeyUpdate. Ideally we'd test a partial ACK, but | 
 | 					// BoringSSL's minimum MTU is such that KeyUpdate always | 
 | 					// fits in one record. | 
 | 					c.WriteACK(c.OutEpoch(), records) | 
 |  | 
 | 					// The shim should now send at the new epoch. Return to the | 
 | 					// test harness, which will enforce this. | 
 | 				}, | 
 | 			}, | 
 | 		}, | 
 | 		shimSendsKeyUpdateBeforeRead: true, | 
 | 	}) | 
 |  | 
 | 	// Test that shim responds to KeyUpdate requests. | 
 | 	fixKeyUpdateReply := func(c *DTLSController, prev, received []DTLSMessage, records []DTLSRecordNumberInfo) { | 
 | 		c.WriteACK(c.OutEpoch(), records) | 
 | 		if received[0].Type != typeKeyUpdate { | 
 | 			return | 
 | 		} | 
 | 		// This works around an awkward testing mismatch. The test | 
 | 		// harness expects the shim to immediately change keys, but | 
 | 		// the shim writes app data before seeing the ACK. The app | 
 | 		// data will be sent at the previous epoch. Consume this and | 
 | 		// prime the shim to resend its reply at the new epoch. | 
 | 		msg := makeTestMessage(int(received[0].Sequence)-2, 32) | 
 | 		c.ReadAppData(c.InEpoch()-1, expectedReply(msg)) | 
 | 		c.WriteAppData(c.OutEpoch(), msg) | 
 | 	} | 
 | 	testCases = append(testCases, testCase{ | 
 | 		protocol: dtls, | 
 | 		name:     "KeyUpdate-Requested-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 			Bugs: ProtocolBugs{ | 
 | 				RejectUnsolicitedKeyUpdate: true, | 
 | 				ACKFlightDTLS:              fixKeyUpdateReply, | 
 | 			}, | 
 | 		}, | 
 | 		// Test the shim receiving many KeyUpdates in a row. They will be | 
 | 		// combined into one reply KeyUpdate. | 
 | 		sendKeyUpdates:   5, | 
 | 		messageLen:       32, | 
 | 		messageCount:     5, | 
 | 		keyUpdateRequest: keyUpdateRequested, | 
 | 	}) | 
 |  | 
 | 	mergeNewSessionTicketAndKeyUpdate := func(f WriteFlightFunc) WriteFlightFunc { | 
 | 		return func(c *DTLSController, prev, received, next []DTLSMessage, records []DTLSRecordNumberInfo) { | 
 | 			// Send NewSessionTicket and the first KeyUpdate all together. | 
 | 			if next[0].Type == typeKeyUpdate { | 
 | 				panic("key update should have been merged into NewSessionTicket") | 
 | 			} | 
 | 			if next[0].Type != typeNewSessionTicket { | 
 | 				c.WriteFlight(next) | 
 | 				return | 
 | 			} | 
 | 			if next[0].Type == typeNewSessionTicket && next[len(next)-1].Type != typeKeyUpdate { | 
 | 				c.MergeIntoNextFlight() | 
 | 				return | 
 | 			} | 
 |  | 
 | 			f(c, prev, received, next, records) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// Test that the shim does not process KeyUpdate until it has processed all | 
 | 	// preceding messages. | 
 | 	testCases = append(testCases, testCase{ | 
 | 		protocol: dtls, | 
 | 		name:     "KeyUpdate-ProcessInOrder-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 			Bugs: ProtocolBugs{ | 
 | 				WriteFlightDTLS: mergeNewSessionTicketAndKeyUpdate(func(c *DTLSController, prev, received, next []DTLSMessage, records []DTLSRecordNumberInfo) { | 
 | 					// Write the KeyUpdate. The shim should buffer and ACK it. | 
 | 					keyUpdate := next[len(next)-1] | 
 | 					c.WriteFlight([]DTLSMessage{keyUpdate}) | 
 | 					ackTimeout := timeouts[0] / 4 | 
 | 					c.AdvanceClock(ackTimeout) | 
 | 					c.ReadACK(c.InEpoch()) | 
 |  | 
 | 					// The shim should not process KeyUpdate yet. It should not | 
 | 					// read from the new epoch. | 
 | 					msg1, msg2 := []byte("aaaa"), []byte("bbbb") | 
 | 					c.WriteAppData(c.OutEpoch(), msg1) | 
 | 					c.AdvanceClock(0) // Check there are no messages. | 
 |  | 
 | 					// It can read from the old epoch, however. | 
 | 					c.WriteAppData(c.OutEpoch()-1, msg2) | 
 | 					c.ReadAppData(c.InEpoch(), expectedReply(msg2)) | 
 |  | 
 | 					// Write the rest of the flight. | 
 | 					c.WriteFlight(next[:len(next)-1]) | 
 | 					c.AdvanceClock(ackTimeout) | 
 | 					c.ReadACK(c.InEpoch()) | 
 |  | 
 | 					// Now the new epoch is functional. | 
 | 					c.WriteAppData(c.OutEpoch(), msg1) | 
 | 					c.ReadAppData(c.InEpoch(), expectedReply(msg1)) | 
 | 				}), | 
 | 			}, | 
 | 		}, | 
 | 		sendKeyUpdates:   1, | 
 | 		keyUpdateRequest: keyUpdateNotRequested, | 
 | 		flags:            []string{"-async"}, | 
 | 	}) | 
 |  | 
 | 	// Messages after a KeyUpdate are not allowed. | 
 | 	testCases = append(testCases, testCase{ | 
 | 		protocol: dtls, | 
 | 		name:     "KeyUpdate-ExtraMessage-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 			Bugs: ProtocolBugs{ | 
 | 				WriteFlightDTLS: mergeNewSessionTicketAndKeyUpdate(func(c *DTLSController, prev, received, next []DTLSMessage, records []DTLSRecordNumberInfo) { | 
 | 					extra := next[0] | 
 | 					extra.Sequence = next[len(next)-1].Sequence + 1 | 
 | 					next = append(slices.Clip(next), extra) | 
 | 					c.WriteFlight(next) | 
 | 				}), | 
 | 			}, | 
 | 		}, | 
 | 		sendKeyUpdates:     1, | 
 | 		keyUpdateRequest:   keyUpdateNotRequested, | 
 | 		shouldFail:         true, | 
 | 		expectedError:      ":EXCESS_HANDSHAKE_DATA:", | 
 | 		expectedLocalError: "remote error: unexpected message", | 
 | 	}) | 
 | 	testCases = append(testCases, testCase{ | 
 | 		protocol: dtls, | 
 | 		name:     "KeyUpdate-ExtraMessageBuffered-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 			Bugs: ProtocolBugs{ | 
 | 				WriteFlightDTLS: mergeNewSessionTicketAndKeyUpdate(func(c *DTLSController, prev, received, next []DTLSMessage, records []DTLSRecordNumberInfo) { | 
 | 					// Send the extra message first. The shim should accept and | 
 | 					// buffer it. | 
 | 					extra := next[0] | 
 | 					extra.Sequence = next[len(next)-1].Sequence + 1 | 
 | 					c.WriteFlight([]DTLSMessage{extra}) | 
 |  | 
 | 					// Now send the flight, including a KeyUpdate. The shim | 
 | 					// should now notice the extra message and reject. | 
 | 					c.WriteFlight(next) | 
 | 				}), | 
 | 			}, | 
 | 		}, | 
 | 		sendKeyUpdates:     1, | 
 | 		keyUpdateRequest:   keyUpdateNotRequested, | 
 | 		shouldFail:         true, | 
 | 		expectedError:      ":EXCESS_HANDSHAKE_DATA:", | 
 | 		expectedLocalError: "remote error: unexpected message", | 
 | 	}) | 
 |  | 
 | 	// Test KeyUpdate overflow conditions. Both the epoch number and the message | 
 | 	// number may overflow, in either the read or write direction. | 
 |  | 
 | 	// When the sender is the client, the first KeyUpdate is message 2 at epoch | 
 | 	// 3, so the epoch number overflows first. | 
 | 	const maxClientKeyUpdates = 0xffff - 3 | 
 |  | 
 | 	// Test that the shim, as a server, rejects KeyUpdates at epoch 0xffff. RFC | 
 | 	// 9147 does not prescribe this limit, but we enforce it. See | 
 | 	// https://mailarchive.ietf.org/arch/msg/tls/6y8wTv8Q_IPM-PCcbCAmDOYg6bM/ | 
 | 	// and https://www.rfc-editor.org/errata/eid8050 | 
 | 	writeFlightKeyUpdate := func(c *DTLSController, prev, received, next []DTLSMessage, records []DTLSRecordNumberInfo) { | 
 | 		if next[0].Type == typeKeyUpdate { | 
 | 			// Exchange some data to avoid tripping KeyUpdate DoS limits. | 
 | 			msg := []byte("test") | 
 | 			c.WriteAppData(c.OutEpoch()-1, msg) | 
 | 			c.ReadAppData(c.InEpoch(), expectedReply(msg)) | 
 | 		} | 
 | 		c.WriteFlight(next) | 
 | 	} | 
 | 	testCases = append(testCases, testCase{ | 
 | 		testType: serverTest, | 
 | 		protocol: dtls, | 
 | 		name:     "KeyUpdate-MaxReadEpoch-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 			Bugs: ProtocolBugs{ | 
 | 				AllowEpochOverflow: true, | 
 | 				WriteFlightDTLS:    writeFlightKeyUpdate, | 
 | 			}, | 
 | 		}, | 
 | 		// Avoid the NewSessionTicket messages interfering with the callback. | 
 | 		flags:            []string{"-no-ticket"}, | 
 | 		sendKeyUpdates:   maxClientKeyUpdates, | 
 | 		keyUpdateRequest: keyUpdateNotRequested, | 
 | 	}) | 
 | 	testCases = append(testCases, testCase{ | 
 | 		testType: serverTest, | 
 | 		protocol: dtls, | 
 | 		name:     "KeyUpdate-ReadEpochOverflow-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 			Bugs: ProtocolBugs{ | 
 | 				AllowEpochOverflow: true, | 
 | 				WriteFlightDTLS:    writeFlightKeyUpdate, | 
 | 			}, | 
 | 		}, | 
 | 		// Avoid the NewSessionTicket messages interfering with the callback. | 
 | 		flags:              []string{"-no-ticket"}, | 
 | 		sendKeyUpdates:     maxClientKeyUpdates + 1, | 
 | 		keyUpdateRequest:   keyUpdateNotRequested, | 
 | 		shouldFail:         true, | 
 | 		expectedError:      ":TOO_MANY_KEY_UPDATES:", | 
 | 		expectedLocalError: "remote error: unexpected message", | 
 | 	}) | 
 |  | 
 | 	// Test that the shim, as a client, notices its epoch overflow condition | 
 | 	// when asked to send too many KeyUpdates. The shim sends KeyUpdate before | 
 | 	// every read, including reading connection close, so the number of | 
 | 	// KeyUpdates is one more than the message count. | 
 | 	testCases = append(testCases, testCase{ | 
 | 		protocol: dtls, | 
 | 		name:     "KeyUpdate-MaxWriteEpoch-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 		}, | 
 | 		shimSendsKeyUpdateBeforeRead: true, | 
 | 		messageCount:                 maxClientKeyUpdates - 1, | 
 | 	}) | 
 | 	testCases = append(testCases, testCase{ | 
 | 		protocol: dtls, | 
 | 		name:     "KeyUpdate-WriteEpochOverflow-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 			Bugs: ProtocolBugs{ | 
 | 				// The shim does not notice the overflow until immediately after | 
 | 				// sending KeyUpdate, so tolerate the overflow on the runner. | 
 | 				AllowEpochOverflow: true, | 
 | 			}, | 
 | 		}, | 
 | 		shimSendsKeyUpdateBeforeRead: true, | 
 | 		messageCount:                 maxClientKeyUpdates, | 
 | 		shouldFail:                   true, | 
 | 		expectedError:                ":TOO_MANY_KEY_UPDATES:", | 
 | 	}) | 
 |  | 
 | 	// When the sender is a server that doesn't send tickets, the first | 
 | 	// KeyUpdate is message 5 (SH, EE, C, CV, Fin) at epoch 3, so the message | 
 | 	// number overflows first. | 
 | 	const maxServerKeyUpdates = 0xffff - 5 | 
 |  | 
 | 	// Test that the shim, as a client, does not allow the value to wraparound. | 
 | 	testCases = append(testCases, testCase{ | 
 | 		protocol: dtls, | 
 | 		name:     "KeyUpdate-ReadMessageOverflow-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion:             VersionTLS13, | 
 | 			SessionTicketsDisabled: true, | 
 | 			Bugs: ProtocolBugs{ | 
 | 				AllowEpochOverflow: true, | 
 | 				WriteFlightDTLS: func(c *DTLSController, prev, received, next []DTLSMessage, records []DTLSRecordNumberInfo) { | 
 | 					writeFlightKeyUpdate(c, prev, received, next, records) | 
 | 					if next[0].Type == typeKeyUpdate && next[0].Sequence == 0xffff { | 
 | 						// At this point, the shim has accepted message 0xffff. | 
 | 						// Check the shim does not now accept message 0 as the | 
 | 						// current message. Test this by sending a garbage | 
 | 						// message 0. A shim that overflows and processes the | 
 | 						// message will notice the syntax error. A shim that | 
 | 						// correctly interprets this as an old message will drop | 
 | 						// the record and simply ACK it. | 
 | 						// | 
 | 						// We do this rather than send a valid KeyUpdate because | 
 | 						// the shim will keep the old epoch active and drop | 
 | 						// decryption failures. Looking for the lack of an error | 
 | 						// is more straightforward. | 
 | 						c.WriteFlight([]DTLSMessage{{Epoch: c.OutEpoch(), Sequence: 0, Type: typeKeyUpdate, Data: []byte("INVALID")}}) | 
 | 						c.ExpectNextTimeout(timeouts[0] / 4) | 
 | 						c.AdvanceClock(timeouts[0] / 4) | 
 | 						c.ReadACK(c.InEpoch()) | 
 | 					} | 
 | 				}, | 
 | 			}, | 
 | 		}, | 
 | 		sendKeyUpdates:   maxServerKeyUpdates + 1, | 
 | 		keyUpdateRequest: keyUpdateNotRequested, | 
 | 		flags:            []string{"-async", "-expect-no-session"}, | 
 | 	}) | 
 |  | 
 | 	// Test that the shim, as a server, notices its message overflow condition, | 
 | 	// when asked to send too many KeyUpdates. | 
 | 	testCases = append(testCases, testCase{ | 
 | 		protocol: dtls, | 
 | 		testType: serverTest, | 
 | 		name:     "KeyUpdate-MaxWriteMessage-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 		}, | 
 | 		shimSendsKeyUpdateBeforeRead: true, | 
 | 		messageCount:                 maxServerKeyUpdates, | 
 | 		// Avoid NewSessionTicket messages getting in the way of ReadKeyUpdate. | 
 | 		flags: []string{"-no-ticket"}, | 
 | 	}) | 
 | 	testCases = append(testCases, testCase{ | 
 | 		protocol: dtls, | 
 | 		testType: serverTest, | 
 | 		name:     "KeyUpdate-WriteMessageOverflow-DTLS", | 
 | 		config: Config{ | 
 | 			MaxVersion: VersionTLS13, | 
 | 		}, | 
 | 		shimSendsKeyUpdateBeforeRead: true, | 
 | 		messageCount:                 maxServerKeyUpdates + 1, | 
 | 		shouldFail:                   true, | 
 | 		expectedError:                ":overflow:", | 
 | 		// Avoid NewSessionTicket messages getting in the way of ReadKeyUpdate. | 
 | 		flags: []string{"-no-ticket"}, | 
 | 	}) | 
 | } |