crypto/tls: support SSLv3
It would be nice not to have to support this since all the clients
that we care about support TLSv1 by now. However, due to buggy
implementations of SSLv3 on the Internet which can't do version
negotiation correctly, browsers will sometimes switch to SSLv3. Since
there's no good way for a browser tell a network problem from a buggy
server, this downgrade can occur even if the server in question is
actually working correctly.
So we need to support SSLv3 for robustness :(
Fixes #1703.
R=bradfitz
CC=golang-dev
https://golang.org/cl/5018045
diff --git a/src/pkg/crypto/tls/prf.go b/src/pkg/crypto/tls/prf.go
index 478cf65..2d58dc5 100644
--- a/src/pkg/crypto/tls/prf.go
+++ b/src/pkg/crypto/tls/prf.go
@@ -63,6 +63,39 @@
}
}
+// pRF30 implements the SSL 3.0 pseudo-random function, as defined in
+// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 6.
+func pRF30(result, secret, label, seed []byte) {
+ hashSHA1 := sha1.New()
+ hashMD5 := md5.New()
+
+ done := 0
+ i := 0
+ // RFC5246 section 6.3 says that the largest PRF output needed is 128
+ // bytes. Since no more ciphersuites will be added to SSLv3, this will
+ // remain true. Each iteration gives us 16 bytes so 10 iterations will
+ // be sufficient.
+ var b [11]byte
+ for done < len(result) {
+ for j := 0; j <= i; j++ {
+ b[j] = 'A' + byte(i)
+ }
+
+ hashSHA1.Reset()
+ hashSHA1.Write(b[:i+1])
+ hashSHA1.Write(secret)
+ hashSHA1.Write(seed)
+ digest := hashSHA1.Sum()
+
+ hashMD5.Reset()
+ hashMD5.Write(secret)
+ hashMD5.Write(digest)
+
+ done += copy(result[done:], hashMD5.Sum())
+ i++
+ }
+}
+
const (
tlsRandomLength = 32 // Length of a random nonce in TLS 1.1.
masterSecretLength = 48 // Length of a master secret in TLS 1.1.
@@ -77,19 +110,24 @@
// keysFromPreMasterSecret generates the connection keys from the pre master
// secret, given the lengths of the MAC key, cipher key and IV, as defined in
// RFC 2246, section 6.3.
-func keysFromPreMasterSecret10(preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
+func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
+ prf := pRF10
+ if version == versionSSL30 {
+ prf = pRF30
+ }
+
var seed [tlsRandomLength * 2]byte
copy(seed[0:len(clientRandom)], clientRandom)
copy(seed[len(clientRandom):], serverRandom)
masterSecret = make([]byte, masterSecretLength)
- pRF10(masterSecret, preMasterSecret, masterSecretLabel, seed[0:])
+ prf(masterSecret, preMasterSecret, masterSecretLabel, seed[0:])
copy(seed[0:len(clientRandom)], serverRandom)
copy(seed[len(serverRandom):], clientRandom)
n := 2*macLen + 2*keyLen + 2*ivLen
keyMaterial := make([]byte, n)
- pRF10(keyMaterial, masterSecret, keyExpansionLabel, seed[0:])
+ prf(keyMaterial, masterSecret, keyExpansionLabel, seed[0:])
clientMAC = keyMaterial[:macLen]
keyMaterial = keyMaterial[macLen:]
serverMAC = keyMaterial[:macLen]
@@ -104,6 +142,10 @@
return
}
+func newFinishedHash(version uint16) finishedHash {
+ return finishedHash{md5.New(), sha1.New(), md5.New(), sha1.New(), version}
+}
+
// A finishedHash calculates the hash of a set of handshake messages suitable
// for including in a Finished message.
type finishedHash struct {
@@ -111,10 +153,7 @@
clientSHA1 hash.Hash
serverMD5 hash.Hash
serverSHA1 hash.Hash
-}
-
-func newFinishedHash() finishedHash {
- return finishedHash{md5.New(), sha1.New(), md5.New(), sha1.New()}
+ version uint16
}
func (h finishedHash) Write(msg []byte) (n int, err os.Error) {
@@ -125,9 +164,10 @@
return len(msg), nil
}
-// finishedSum calculates the contents of the verify_data member of a Finished
-// message given the MD5 and SHA1 hashes of a set of handshake messages.
-func finishedSum(md5, sha1, label, masterSecret []byte) []byte {
+// finishedSum10 calculates the contents of the verify_data member of a TLSv1
+// Finished message given the MD5 and SHA1 hashes of a set of handshake
+// messages.
+func finishedSum10(md5, sha1, label, masterSecret []byte) []byte {
seed := make([]byte, len(md5)+len(sha1))
copy(seed, md5)
copy(seed[len(md5):], sha1)
@@ -136,18 +176,61 @@
return out
}
+// finishedSum30 calculates the contents of the verify_data member of a SSLv3
+// Finished message given the MD5 and SHA1 hashes of a set of handshake
+// messages.
+func finishedSum30(md5, sha1 hash.Hash, masterSecret []byte, magic [4]byte) []byte {
+ md5.Write(magic[:])
+ md5.Write(masterSecret)
+ md5.Write(ssl30Pad1[:])
+ md5Digest := md5.Sum()
+
+ md5.Reset()
+ md5.Write(masterSecret)
+ md5.Write(ssl30Pad2[:])
+ md5.Write(md5Digest)
+ md5Digest = md5.Sum()
+
+ sha1.Write(magic[:])
+ sha1.Write(masterSecret)
+ sha1.Write(ssl30Pad1[:40])
+ sha1Digest := sha1.Sum()
+
+ sha1.Reset()
+ sha1.Write(masterSecret)
+ sha1.Write(ssl30Pad2[:40])
+ sha1.Write(sha1Digest)
+ sha1Digest = sha1.Sum()
+
+ ret := make([]byte, len(md5Digest)+len(sha1Digest))
+ copy(ret, md5Digest)
+ copy(ret[len(md5Digest):], sha1Digest)
+ return ret
+}
+
+var ssl3ClientFinishedMagic = [4]byte{0x43, 0x4c, 0x4e, 0x54}
+var ssl3ServerFinishedMagic = [4]byte{0x53, 0x52, 0x56, 0x52}
+
// clientSum returns the contents of the verify_data member of a client's
// Finished message.
func (h finishedHash) clientSum(masterSecret []byte) []byte {
+ if h.version == versionSSL30 {
+ return finishedSum30(h.clientMD5, h.clientSHA1, masterSecret, ssl3ClientFinishedMagic)
+ }
+
md5 := h.clientMD5.Sum()
sha1 := h.clientSHA1.Sum()
- return finishedSum(md5, sha1, clientFinishedLabel, masterSecret)
+ return finishedSum10(md5, sha1, clientFinishedLabel, masterSecret)
}
// serverSum returns the contents of the verify_data member of a server's
// Finished message.
func (h finishedHash) serverSum(masterSecret []byte) []byte {
+ if h.version == versionSSL30 {
+ return finishedSum30(h.serverMD5, h.serverSHA1, masterSecret, ssl3ServerFinishedMagic)
+ }
+
md5 := h.serverMD5.Sum()
sha1 := h.serverSHA1.Sum()
- return finishedSum(md5, sha1, serverFinishedLabel, masterSecret)
+ return finishedSum10(md5, sha1, serverFinishedLabel, masterSecret)
}