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