Test that DTLS 1.2 rejects renegotiation

We do not support renegotiation in DTLS at all. This falls out of us
generically rejecting all unexpected handshake records after the
handshake, but test this more explicitly.

DTLS 1.2 renegotiation is particularly weird because they reset the
message sequence number, which actually makes message reassembly
ambiguous! One cannot *just* use the sequence number state to
distinguish between retransmit of a past message and a new message.

We currently assume fragments are renegotiation attempts by default, and
special-case Finished as a retransmit. If we were went the other way[*],
we should have a test that covers the new messages.

[*] Going the other way may be a little tidier if we want to implement
crbug.com/383016430. We also currently would break if we ever enabled
NPN or Channel ID in DTLS.

Change-Id: I9f304d0f7ea356fb67e63e22ee06d1ed0fcaa804
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/86987
Commit-Queue: Lily Chen <chlily@google.com>
Reviewed-by: Lily Chen <chlily@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go
index d9a1051..6f11a56 100644
--- a/ssl/test/runner/conn.go
+++ b/ssl/test/runner/conn.go
@@ -1741,6 +1741,16 @@
 }
 
 func (c *Conn) Renegotiate() error {
+	if c.vers >= VersionTLS13 {
+		return errors.New("tls: renegotiation requires (D)TLS 1.2 or earlier")
+	}
+
+	// In DTLS, renegotiation resets the message sequence numbers.
+	if c.isDTLS {
+		c.sendHandshakeSeq = 0
+		c.recvHandshakeSeq = 0
+	}
+
 	if !c.isClient {
 		helloReq := new(helloRequestMsg).marshal()
 		if c.config.Bugs.BadHelloRequest != nil {
diff --git a/ssl/test/runner/renegotiation_tests.go b/ssl/test/runner/renegotiation_tests.go
index 02dbc19..a0fd152 100644
--- a/ssl/test/runner/renegotiation_tests.go
+++ b/ssl/test/runner/renegotiation_tests.go
@@ -543,4 +543,33 @@
 			"-expect-verify-result",
 		},
 	})
+
+	// We do not support renegotiation in DTLS, even if enabled. Currently,
+	// BoringSSL treats this as an unexpected record.
+	testCases = append(testCases, testCase{
+		protocol: dtls,
+		testType: clientTest,
+		name:     "Renegotiate-DTLS-Client-Forbidden",
+		config: Config{
+			MaxVersion: VersionTLS12,
+		},
+		renegotiate:        1,
+		flags:              []string{"-renegotiate-freely"},
+		shouldFail:         true,
+		expectedError:      ":UNEXPECTED_RECORD:",
+		expectedLocalError: "remote error: unexpected message",
+	})
+	testCases = append(testCases, testCase{
+		protocol: dtls,
+		testType: serverTest,
+		name:     "Renegotiate-DTLS-Server-Forbidden",
+		config: Config{
+			MaxVersion: VersionTLS12,
+		},
+		renegotiate:        1,
+		flags:              []string{"-renegotiate-freely"},
+		shouldFail:         true,
+		expectedError:      ":UNEXPECTED_RECORD:",
+		expectedLocalError: "remote error: unexpected message",
+	})
 }