diff options
Diffstat (limited to 'libgo/go/crypto/tls/conn.go')
-rw-r--r-- | libgo/go/crypto/tls/conn.go | 192 |
1 files changed, 150 insertions, 42 deletions
diff --git a/libgo/go/crypto/tls/conn.go b/libgo/go/crypto/tls/conn.go index d8c2be00a26..2e64b88a689 100644 --- a/libgo/go/crypto/tls/conn.go +++ b/libgo/go/crypto/tls/conn.go @@ -146,6 +146,9 @@ func (hc *halfConn) changeCipherSpec() error { hc.mac = hc.nextMac hc.nextCipher = nil hc.nextMac = nil + for i := range hc.seq { + hc.seq[i] = 0 + } return nil } @@ -229,8 +232,16 @@ func roundUp(a, b int) int { return a + (b-a%b)%b } -// decrypt checks and strips the mac and decrypts the data in b. -func (hc *halfConn) decrypt(b *block) (bool, alert) { +// 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 b. Returns a +// success boolean, the number of bytes to skip from the start of the record in +// order to get the application payload, and an optional alert value. +func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert) { // pull out payload payload := b.data[recordHeaderLen:] @@ -240,26 +251,54 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) { } paddingGood := byte(255) + explicitIVLen := 0 // decrypt if hc.cipher != nil { switch c := hc.cipher.(type) { case cipher.Stream: c.XORKeyStream(payload, payload) - case cipher.BlockMode: + case cipher.AEAD: + explicitIVLen = 8 + if len(payload) < explicitIVLen { + return false, 0, alertBadRecordMAC + } + nonce := payload[:8] + payload = payload[8:] + + var additionalData [13]byte + copy(additionalData[:], hc.seq[:]) + copy(additionalData[8:], b.data[:3]) + n := len(payload) - c.Overhead() + additionalData[11] = byte(n >> 8) + additionalData[12] = byte(n) + var err error + payload, err = c.Open(payload[:0], nonce, payload, additionalData[:]) + if err != nil { + return false, 0, alertBadRecordMAC + } + b.resize(recordHeaderLen + explicitIVLen + len(payload)) + case cbcMode: blockSize := c.BlockSize() + if hc.version >= VersionTLS11 { + explicitIVLen = blockSize + } - if len(payload)%blockSize != 0 || len(payload) < roundUp(macSize+1, blockSize) { - return false, alertBadRecordMAC + if len(payload)%blockSize != 0 || len(payload) < roundUp(explicitIVLen+macSize+1, blockSize) { + return false, 0, alertBadRecordMAC } + if explicitIVLen > 0 { + c.SetIV(payload[:explicitIVLen]) + payload = payload[explicitIVLen:] + } c.CryptBlocks(payload, payload) - if hc.version == versionSSL30 { + if hc.version == VersionSSL30 { payload, paddingGood = removePaddingSSL30(payload) } else { payload, paddingGood = removePadding(payload) } - b.resize(recordHeaderLen + len(payload)) + b.resize(recordHeaderLen + explicitIVLen + len(payload)) // note that we still have a timing side-channel in the // MAC check, below. An attacker can align the record @@ -279,25 +318,25 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) { // check, strip mac if hc.mac != nil { if len(payload) < macSize { - return false, alertBadRecordMAC + return false, 0, alertBadRecordMAC } // strip mac off payload, b.data n := len(payload) - macSize b.data[3] = byte(n >> 8) b.data[4] = byte(n) - b.resize(recordHeaderLen + n) + b.resize(recordHeaderLen + explicitIVLen + n) remoteMAC := payload[n:] - localMAC := hc.mac.MAC(hc.inDigestBuf, hc.seq[0:], b.data) - hc.incSeq() + localMAC := hc.mac.MAC(hc.inDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], payload[:n]) if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 { - return false, alertBadRecordMAC + return false, 0, alertBadRecordMAC } hc.inDigestBuf = localMAC } + hc.incSeq() - return true, 0 + return true, recordHeaderLen + explicitIVLen, 0 } // padToBlockSize calculates the needed padding block, if any, for a payload. @@ -318,11 +357,10 @@ func padToBlockSize(payload []byte, blockSize int) (prefix, finalBlock []byte) { } // encrypt encrypts and macs the data in b. -func (hc *halfConn) encrypt(b *block) (bool, alert) { +func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) { // mac if hc.mac != nil { - mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data) - hc.incSeq() + mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], b.data[recordHeaderLen+explicitIVLen:]) n := len(b.data) b.resize(n + len(mac)) @@ -337,11 +375,30 @@ func (hc *halfConn) encrypt(b *block) (bool, alert) { switch c := hc.cipher.(type) { case cipher.Stream: c.XORKeyStream(payload, payload) - case cipher.BlockMode: - prefix, finalBlock := padToBlockSize(payload, c.BlockSize()) - b.resize(recordHeaderLen + len(prefix) + len(finalBlock)) - c.CryptBlocks(b.data[recordHeaderLen:], prefix) - c.CryptBlocks(b.data[recordHeaderLen+len(prefix):], finalBlock) + case cipher.AEAD: + payloadLen := len(b.data) - recordHeaderLen - explicitIVLen + b.resize(len(b.data) + c.Overhead()) + nonce := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] + payload := b.data[recordHeaderLen+explicitIVLen:] + payload = payload[:payloadLen] + + var additionalData [13]byte + copy(additionalData[:], hc.seq[:]) + copy(additionalData[8:], b.data[:3]) + additionalData[11] = byte(payloadLen >> 8) + additionalData[12] = byte(payloadLen) + + c.Seal(payload[:0], nonce, payload, additionalData[:]) + case cbcMode: + blockSize := c.BlockSize() + if explicitIVLen > 0 { + c.SetIV(payload[:explicitIVLen]) + payload = payload[explicitIVLen:] + } + prefix, finalBlock := padToBlockSize(payload, blockSize) + b.resize(recordHeaderLen + explicitIVLen + len(prefix) + len(finalBlock)) + c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen:], prefix) + c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen+len(prefix):], finalBlock) default: panic("unknown cipher type") } @@ -351,6 +408,7 @@ func (hc *halfConn) encrypt(b *block) (bool, alert) { n := len(b.data) - recordHeaderLen b.data[3] = byte(n >> 8) b.data[4] = byte(n) + hc.incSeq() return true, 0 } @@ -534,10 +592,11 @@ Again: // Process message. b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n) - b.off = recordHeaderLen - if ok, err := c.in.decrypt(b); !ok { + ok, off, err := c.in.decrypt(b) + if !ok { return c.sendAlert(err) } + b.off = off data := b.data[b.off:] if len(data) > maxPlaintext { c.sendAlert(alertRecordOverflow) @@ -637,18 +696,52 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) { if m > maxPlaintext { m = maxPlaintext } - b.resize(recordHeaderLen + m) + explicitIVLen := 0 + explicitIVIsSeq := false + + var cbc cbcMode + if c.out.version >= VersionTLS11 { + var ok bool + if cbc, ok = c.out.cipher.(cbcMode); ok { + explicitIVLen = cbc.BlockSize() + } + } + if explicitIVLen == 0 { + if _, ok := c.out.cipher.(cipher.AEAD); ok { + explicitIVLen = 8 + // The AES-GCM construction in TLS has an + // explicit nonce so that the nonce can be + // random. However, the nonce is only 8 bytes + // which is too small for a secure, random + // nonce. Therefore we use the sequence number + // as the nonce. + explicitIVIsSeq = true + } + } + b.resize(recordHeaderLen + explicitIVLen + m) b.data[0] = byte(typ) vers := c.vers if vers == 0 { - vers = maxVersion + // Some TLS servers fail if the record version is + // greater than TLS 1.0 for the initial ClientHello. + vers = VersionTLS10 } b.data[1] = byte(vers >> 8) b.data[2] = byte(vers) b.data[3] = byte(m >> 8) b.data[4] = byte(m) - copy(b.data[recordHeaderLen:], data) - c.out.encrypt(b) + if explicitIVLen > 0 { + explicitIV := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] + if explicitIVIsSeq { + copy(explicitIV, c.out.seq[:]) + } else { + if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil { + break + } + } + } + copy(b.data[recordHeaderLen+explicitIVLen:], data) + c.out.encrypt(b, explicitIVLen) _, err = c.conn.Write(b.data) if err != nil { break @@ -709,7 +802,9 @@ func (c *Conn) readHandshake() (interface{}, error) { case typeCertificate: m = new(certificateMsg) case typeCertificateRequest: - m = new(certificateRequestMsg) + m = &certificateRequestMsg{ + hasSignatureAndHash: c.vers >= VersionTLS12, + } case typeCertificateStatus: m = new(certificateStatusMsg) case typeServerKeyExchange: @@ -719,7 +814,9 @@ func (c *Conn) readHandshake() (interface{}, error) { case typeClientKeyExchange: m = new(clientKeyExchangeMsg) case typeCertificateVerify: - m = new(certificateVerifyMsg) + m = &certificateVerifyMsg{ + hasSignatureAndHash: c.vers >= VersionTLS12, + } case typeNextProtocol: m = new(nextProtoMsg) case typeFinished: @@ -768,7 +865,7 @@ func (c *Conn) Write(b []byte) (int, error) { // http://www.imperialviolet.org/2012/01/15/beastfollowup.html var m int - if len(b) > 1 && c.vers <= versionTLS10 { + if len(b) > 1 && c.vers <= VersionTLS10 { if _, ok := c.out.cipher.(cipher.BlockMode); ok { n, err := c.writeRecord(recordTypeApplicationData, b[:1]) if err != nil { @@ -792,21 +889,32 @@ func (c *Conn) Read(b []byte) (n int, err error) { c.in.Lock() defer c.in.Unlock() - for c.input == nil && c.error() == nil { - if err := c.readRecord(recordTypeApplicationData); err != nil { - // Soft error, like EAGAIN + // Some OpenSSL servers send empty records in order to randomize the + // CBC IV. So this loop ignores a limited number of empty records. + const maxConsecutiveEmptyRecords = 100 + for emptyRecordCount := 0; emptyRecordCount <= maxConsecutiveEmptyRecords; emptyRecordCount++ { + for c.input == nil && c.error() == nil { + if err := c.readRecord(recordTypeApplicationData); err != nil { + // Soft error, like EAGAIN + return 0, err + } + } + if err := c.error(); err != nil { return 0, err } + + n, err = c.input.Read(b) + if c.input.off >= len(c.input.data) { + c.in.freeBlock(c.input) + c.input = nil + } + + if n != 0 || err != nil { + return n, err + } } - if err := c.error(); err != nil { - return 0, err - } - n, err = c.input.Read(b) - if c.input.off >= len(c.input.data) { - c.in.freeBlock(c.input) - c.input = nil - } - return n, nil + + return 0, io.ErrNoProgress } // Close closes the connection. |