Fix test of first of 255 CBC padding bytes.
Thanks to Peter Gijsels for pointing out that if a CBC record has 255
bytes of padding, the first was not being checked.
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index 9812bde..0905e9f 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -332,6 +332,15 @@
// can be invalid.
BadECDSAR BadValue
BadECDSAS BadValue
+
+ // MaxPadding causes CBC records to have the maximum possible padding.
+ MaxPadding bool
+ // PaddingFirstByteBad causes the first byte of the padding to be
+ // incorrect.
+ PaddingFirstByteBad bool
+ // PaddingFirstByteBadIf255 causes the first byte of padding to be
+ // incorrect if there's a maximum amount of padding (i.e. 255 bytes).
+ PaddingFirstByteBadIf255 bool
}
func (c *Config) serverInit() {
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go
index d130895..49224a2 100644
--- a/ssl/test/runner/conn.go
+++ b/ssl/test/runner/conn.go
@@ -106,6 +106,8 @@
// used to save allocating a new buffer for each MAC.
inDigestBuf, outDigestBuf []byte
+
+ config *Config
}
func (hc *halfConn) setErrorLocked(err error) error {
@@ -130,7 +132,7 @@
// changeCipherSpec changes the encryption and MAC states
// to the ones previously passed to prepareCipherSpec.
-func (hc *halfConn) changeCipherSpec() error {
+func (hc *halfConn) changeCipherSpec(config *Config) error {
if hc.nextCipher == nil {
return alertInternalError
}
@@ -138,6 +140,7 @@
hc.mac = hc.nextMac
hc.nextCipher = nil
hc.nextMac = nil
+ hc.config = config
for i := range hc.seq {
hc.seq[i] = 0
}
@@ -336,15 +339,26 @@
// block of payload. finalBlock is a fresh slice which contains the contents of
// any suffix of payload as well as the needed padding to make finalBlock a
// full block.
-func padToBlockSize(payload []byte, blockSize int) (prefix, finalBlock []byte) {
+func padToBlockSize(payload []byte, blockSize int, config *Config) (prefix, finalBlock []byte) {
overrun := len(payload) % blockSize
- paddingLen := blockSize - overrun
prefix = payload[:len(payload)-overrun]
- finalBlock = make([]byte, blockSize)
- copy(finalBlock, payload[len(payload)-overrun:])
- for i := overrun; i < blockSize; i++ {
+
+ paddingLen := blockSize - overrun
+ finalSize := blockSize
+ if config.Bugs.MaxPadding {
+ for paddingLen+blockSize <= 256 {
+ paddingLen += blockSize
+ }
+ finalSize = 256
+ }
+ finalBlock = make([]byte, finalSize)
+ for i := range finalBlock {
finalBlock[i] = byte(paddingLen - 1)
}
+ if config.Bugs.PaddingFirstByteBad || config.Bugs.PaddingFirstByteBadIf255 && paddingLen == 256 {
+ finalBlock[overrun] ^= 0xff
+ }
+ copy(finalBlock, payload[len(payload)-overrun:])
return
}
@@ -387,7 +401,7 @@
c.SetIV(payload[:explicitIVLen])
payload = payload[explicitIVLen:]
}
- prefix, finalBlock := padToBlockSize(payload, blockSize)
+ prefix, finalBlock := padToBlockSize(payload, blockSize, hc.config)
b.resize(recordHeaderLen + explicitIVLen + len(prefix) + len(finalBlock))
c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen:], prefix)
c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen+len(prefix):], finalBlock)
@@ -633,7 +647,7 @@
c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
break
}
- err := c.in.changeCipherSpec()
+ err := c.in.changeCipherSpec(c.config)
if err != nil {
c.in.setErrorLocked(c.sendAlert(err.(alert)))
}
@@ -752,7 +766,7 @@
c.out.freeBlock(b)
if typ == recordTypeChangeCipherSpec {
- err = c.out.changeCipherSpec()
+ err = c.out.changeCipherSpec(c.config)
if err != nil {
// Cannot call sendAlert directly,
// because we already hold c.out.Mutex.
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 1edd00b..b5743c5 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -47,6 +47,9 @@
config Config
shouldFail bool
expectedError string
+ // messageLen is the length, in bytes, of the test message that will be
+ // sent.
+ messageLen int
}
var clientTests = []testCase{
@@ -87,12 +90,17 @@
},
}
-var testMessage = []byte("testing")
-
-func doExchange(tlsConn *Conn) error {
+func doExchange(tlsConn *Conn, messageLen int) error {
if err := tlsConn.Handshake(); err != nil {
return err
}
+ if messageLen == 0 {
+ messageLen = 32
+ }
+ testMessage := make([]byte, messageLen)
+ for i := range testMessage {
+ testMessage[i] = 0x42
+ }
tlsConn.Write(testMessage)
buf := make([]byte, len(testMessage))
@@ -168,7 +176,7 @@
}
tlsConn := Server(conn, &config)
- err = doExchange(tlsConn)
+ err = doExchange(tlsConn, test.messageLen)
conn.Close()
childErr := client.Wait()
@@ -288,6 +296,45 @@
}
}
+func addCBCPaddingTests() {
+ clientTests = append(clientTests, testCase{
+ name: "MaxCBCPadding",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
+ Bugs: ProtocolBugs{
+ MaxPadding: true,
+ },
+ },
+ messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
+ })
+ clientTests = append(clientTests, testCase{
+ name: "BadCBCPadding",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
+ Bugs: ProtocolBugs{
+ PaddingFirstByteBad: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
+ })
+ // OpenSSL previously had an issue where the first byte of padding in
+ // 255 bytes of padding wasn't checked.
+ clientTests = append(clientTests, testCase{
+ name: "BadCBCPadding255",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
+ Bugs: ProtocolBugs{
+ MaxPadding: true,
+ PaddingFirstByteBadIf255: true,
+ },
+ },
+ messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
+ shouldFail: true,
+ expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
+ })
+}
+
func worker(statusChan chan statusMsg, c chan *testCase, wg *sync.WaitGroup) {
defer wg.Done()
@@ -334,6 +381,7 @@
addCipherSuiteTests()
addBadECDSASignatureTests()
+ addCBCPaddingTests()
var wg sync.WaitGroup