Skip to content

Commit d7df9de

Browse files
davidbenFiloSottile
authored andcommitted
crypto/tls: fix a minor MAC vs padding leak
The CBC mode ciphers in TLS are a disaster. By ordering authentication and encryption wrong, they are very subtly dependent on details and implementation of the padding check, admitting attacks such as POODLE and Lucky13. crypto/tls does not promise full countermeasures for Lucky13 and still contains some timing variations. This change fixes one of the easy ones: by checking the MAC, then the padding, rather than all at once, there is a very small timing variation between bad MAC and (good MAC, bad padding). The consequences depend on the effective padding value used in the MAC when the padding is bad. extractPadding simply uses the last byte's value, leaving the padding bytes effectively unchecked. This is the scenario in SSL 3.0 that led to POODLE. Specifically, the attacker can take an input record which uses 16 bytes of padding (a full block) and replace the final block with some interesting block. The MAC check will succeed with 1/256 probability due to the final byte being 16. This again means that after 256 queries, the attacker can decrypt one byte. To fix this, bitwise AND the two values so they may be checked with one branch. Additionally, zero the padding if the padding check failed, to make things more robust. Updates #27071 Change-Id: I332b14d215078928ffafe3cfeba1a68189f08db3 Reviewed-on: https://go-review.googlesource.com/c/go/+/170701 Reviewed-by: Filippo Valsorda <[email protected]> Run-TryBot: Filippo Valsorda <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 850844e commit d7df9de

File tree

1 file changed

+20
-1
lines changed

1 file changed

+20
-1
lines changed

src/crypto/tls/conn.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,17 @@ func extractPadding(payload []byte) (toRemove int, good byte) {
274274
good &= good << 1
275275
good = uint8(int8(good) >> 7)
276276

277+
// Zero the padding length on error. This ensures any unchecked bytes
278+
// are included in the MAC. Otherwise, an attacker that could
279+
// distinguish MAC failures from padding failures could mount an attack
280+
// similar to POODLE in SSL 3.0: given a good ciphertext that uses a
281+
// full block's worth of padding, replace the final block with another
282+
// block. If the MAC check passed but the padding check failed, the
283+
// last byte of that block decrypted to the block size.
284+
//
285+
// See also macAndPaddingGood logic below.
286+
paddingLen &= good
287+
277288
toRemove = int(paddingLen) + 1
278289
return
279290
}
@@ -416,7 +427,15 @@ func (hc *halfConn) decrypt(record []byte) ([]byte, recordType, error) {
416427
remoteMAC := payload[n : n+macSize]
417428
localMAC := hc.mac.MAC(hc.seq[0:], record[:recordHeaderLen], payload[:n], payload[n+macSize:])
418429

419-
if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 {
430+
// This is equivalent to checking the MACs and paddingGood
431+
// separately, but in constant-time to prevent distinguishing
432+
// padding failures from MAC failures. Depending on what value
433+
// of paddingLen was returned on bad padding, distinguishing
434+
// bad MAC from bad padding can lead to an attack.
435+
//
436+
// See also the logic at the end of extractPadding.
437+
macAndPaddingGood := subtle.ConstantTimeCompare(localMAC, remoteMAC) & int(paddingGood)
438+
if macAndPaddingGood != 1 {
420439
return nil, 0, alertBadRecordMAC
421440
}
422441

0 commit comments

Comments
 (0)