runner: don't assume BlockMod implements SetIV

It's an undocumented implementation detail, inherited from crypto/tls.

We are probably dropping it from the NewCBC* implementations (if it
doesn't break too many applications).

Change-Id: I2e27bc166bd4c75c449bde6980c68772d9a9191a
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/72947
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: Bob Beck <bbe@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/test/runner/cipher_suites.go b/ssl/test/runner/cipher_suites.go
index c43fc09..56626c0 100644
--- a/ssl/test/runner/cipher_suites.go
+++ b/ssl/test/runner/cipher_suites.go
@@ -155,20 +155,37 @@
 	return nullCipher{}
 }
 
+type cbcMode struct {
+	cipher.BlockMode
+	new func(iv []byte) cipher.BlockMode
+}
+
+func (c *cbcMode) SetIV(iv []byte) {
+	c.BlockMode = c.new(iv)
+}
+
 func cipher3DES(key, iv []byte, isRead bool) any {
+	c := &cbcMode{}
 	block, _ := des.NewTripleDESCipher(key)
 	if isRead {
-		return cipher.NewCBCDecrypter(block, iv)
+		c.new = func(iv []byte) cipher.BlockMode { return cipher.NewCBCDecrypter(block, iv) }
+	} else {
+		c.new = func(iv []byte) cipher.BlockMode { return cipher.NewCBCEncrypter(block, iv) }
 	}
-	return cipher.NewCBCEncrypter(block, iv)
+	c.SetIV(iv)
+	return c
 }
 
 func cipherAES(key, iv []byte, isRead bool) any {
+	c := &cbcMode{}
 	block, _ := aes.NewCipher(key)
 	if isRead {
-		return cipher.NewCBCDecrypter(block, iv)
+		c.new = func(iv []byte) cipher.BlockMode { return cipher.NewCBCDecrypter(block, iv) }
+	} else {
+		c.new = func(iv []byte) cipher.BlockMode { return cipher.NewCBCEncrypter(block, iv) }
 	}
-	return cipher.NewCBCEncrypter(block, iv)
+	c.SetIV(iv)
+	return c
 }
 
 // macSHA1 returns a macFunction for the given protocol version.
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go
index 167ab13..d63242e 100644
--- a/ssl/test/runner/conn.go
+++ b/ssl/test/runner/conn.go
@@ -391,7 +391,7 @@
 			return 8
 		}
 		return 0
-	case cbcMode:
+	case *cbcMode:
 		if hc.version >= VersionTLS11 || hc.isDTLS {
 			return c.BlockSize()
 		}
@@ -450,12 +450,6 @@
 	return a + (b-a%b)%b
 }
 
-// cbcMode is an interface for block ciphers using cipher block chaining.
-type cbcMode interface {
-	cipher.BlockMode
-	SetIV([]byte)
-}
-
 // decrypt checks and strips the mac and decrypts the data in record. Returns a
 // success boolean, the application payload, the encrypted record type (or 0
 // if there is none), and an optional alert value. Decryption occurs in-place,
@@ -511,7 +505,7 @@
 			if err != nil {
 				return false, 0, nil, alertBadRecordMAC
 			}
-		case cbcMode:
+		case *cbcMode:
 			blockSize := c.BlockSize()
 			if len(payload)%blockSize != 0 || len(payload) < roundUp(explicitIVLen+macSize+1, blockSize) {
 				return false, 0, nil, alertBadRecordMAC
@@ -627,7 +621,7 @@
 		case cipher.Stream, *nullCipher:
 		case *tlsAead:
 			overhead += c.Overhead()
-		case cbcMode:
+		case *cbcMode:
 			overhead += computingCBCPaddingLength(payloadLen+macSize, c.BlockSize(), hc.config)
 		case nullCipher:
 			break
@@ -721,7 +715,7 @@
 			}
 
 			record = c.Seal(record[:prefixLen+explicitIVLen], nonce, record[prefixLen+explicitIVLen:], additionalData)
-		case cbcMode:
+		case *cbcMode:
 			if explicitIVLen > 0 {
 				if _, err := io.ReadFull(hc.config.rand(), explicitIV); err != nil {
 					return nil, err
@@ -1602,7 +1596,7 @@
 
 	var m int
 	if len(b) > 1 && c.vers <= VersionTLS10 && !c.isDTLS {
-		if _, ok := c.out.epoch.cipher.(cipher.BlockMode); ok {
+		if _, ok := c.out.epoch.cipher.(*cbcMode); ok {
 			n, err := c.writeRecord(recordTypeApplicationData, b[:1])
 			if err != nil {
 				return n, c.out.setErrorLocked(err)
diff --git a/ssl/test/runner/dtls.go b/ssl/test/runner/dtls.go
index 87a8081..2f993d1 100644
--- a/ssl/test/runner/dtls.go
+++ b/ssl/test/runner/dtls.go
@@ -247,7 +247,7 @@
 		// Save information about the current record, including how many more
 		// bytes the shim could have added.
 		recordBytesAvailable := c.bytesAvailableInPacket + c.rawInput.Len()
-		if cbc, ok := epoch.cipher.(cbcMode); ok {
+		if cbc, ok := epoch.cipher.(*cbcMode); ok {
 			// It is possible that adding a byte would have added another block.
 			recordBytesAvailable = max(0, recordBytesAvailable-cbc.BlockSize())
 		}