forked from quic-go/quic-go
validate version tag in CHLO to prevent version downgrade attacks
fixes #360
This commit is contained in:
committed by
Lucas Clemente
parent
f72154e30b
commit
d39c2a3027
@@ -3,6 +3,7 @@ package handshake
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
@@ -102,10 +103,21 @@ func (h *CryptoSetup) handleMessage(chloData []byte, cryptoData map[Tag][]byte)
|
||||
return false, qerr.Error(qerr.CryptoMessageParameterNotFound, "SNI required")
|
||||
}
|
||||
|
||||
_, ok = cryptoData[TagVER]
|
||||
// prevent version downgrade attacks
|
||||
// see https://groups.google.com/a/chromium.org/forum/#!topic/proto-quic/N-de9j63tCk for a discussion and examples
|
||||
verSlice, ok := cryptoData[TagVER]
|
||||
if !ok {
|
||||
return false, qerr.Error(qerr.InvalidCryptoMessageParameter, "client hello missing version tag")
|
||||
}
|
||||
if len(verSlice) != 4 {
|
||||
return false, qerr.Error(qerr.InvalidCryptoMessageParameter, "incorrect version tag")
|
||||
}
|
||||
verTag := binary.LittleEndian.Uint32(verSlice)
|
||||
ver := protocol.VersionTagToNumber(verTag)
|
||||
// If the client's preferred version is not the version we are currently speaking, then the client went through a version negotiation. In this case, we need to make sure that we actually do not support this version and that it wasn't a downgrade attack.
|
||||
if ver != h.version && protocol.IsSupportedVersion(ver) {
|
||||
return false, qerr.Error(qerr.VersionNegotiationMismatch, "Downgrade attack detected")
|
||||
}
|
||||
|
||||
var reply []byte
|
||||
var err error
|
||||
|
||||
@@ -359,6 +359,63 @@ var _ = Describe("Crypto setup", func() {
|
||||
Expect(err).To(MatchError(qerr.Error(qerr.InvalidCryptoMessageParameter, "client hello missing version tag")))
|
||||
})
|
||||
|
||||
It("rejects CHLOs with a version tag that has the wrong length", func() {
|
||||
WriteHandshakeMessage(&stream.dataToRead, TagCHLO, map[Tag][]byte{
|
||||
TagSCID: scfg.ID,
|
||||
TagSNI: []byte("quic.clemente.io"),
|
||||
TagPUBS: []byte("pubs"),
|
||||
TagNONC: nonce32,
|
||||
TagSTK: validSTK,
|
||||
TagKEXS: kexs,
|
||||
TagAEAD: aead,
|
||||
TagVER: []byte{0x13, 0x37}, // should be 4 bytes
|
||||
})
|
||||
err := cs.HandleCryptoStream()
|
||||
Expect(err).To(MatchError(qerr.Error(qerr.InvalidCryptoMessageParameter, "incorrect version tag")))
|
||||
})
|
||||
|
||||
It("detects version downgrade attacks", func() {
|
||||
highestSupportedVersion := protocol.SupportedVersions[len(protocol.SupportedVersions)-1]
|
||||
lowestSupportedVersion := protocol.SupportedVersions[0]
|
||||
Expect(highestSupportedVersion).ToNot(Equal(lowestSupportedVersion))
|
||||
cs.version = highestSupportedVersion
|
||||
b := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, protocol.VersionNumberToTag(lowestSupportedVersion))
|
||||
WriteHandshakeMessage(&stream.dataToRead, TagCHLO, map[Tag][]byte{
|
||||
TagSCID: scfg.ID,
|
||||
TagSNI: []byte("quic.clemente.io"),
|
||||
TagPUBS: []byte("pubs"),
|
||||
TagNONC: nonce32,
|
||||
TagSTK: validSTK,
|
||||
TagKEXS: kexs,
|
||||
TagAEAD: aead,
|
||||
TagVER: b,
|
||||
})
|
||||
err := cs.HandleCryptoStream()
|
||||
Expect(err).To(MatchError(qerr.Error(qerr.VersionNegotiationMismatch, "Downgrade attack detected")))
|
||||
})
|
||||
|
||||
It("accepts a non-matching version tag in the CHLO, if it is an unsupported version", func() {
|
||||
supportedVersion := protocol.SupportedVersions[0]
|
||||
unsupportedVersion := supportedVersion + 1000
|
||||
Expect(protocol.IsSupportedVersion(unsupportedVersion)).To(BeFalse())
|
||||
cs.version = supportedVersion
|
||||
b := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, protocol.VersionNumberToTag(unsupportedVersion))
|
||||
WriteHandshakeMessage(&stream.dataToRead, TagCHLO, map[Tag][]byte{
|
||||
TagSCID: scfg.ID,
|
||||
TagSNI: []byte("quic.clemente.io"),
|
||||
TagPUBS: []byte("pubs"),
|
||||
TagNONC: nonce32,
|
||||
TagSTK: validSTK,
|
||||
TagKEXS: kexs,
|
||||
TagAEAD: aead,
|
||||
TagVER: b,
|
||||
})
|
||||
err := cs.HandleCryptoStream()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("errors if the AEAD tag is missing", func() {
|
||||
WriteHandshakeMessage(&stream.dataToRead, TagCHLO, map[Tag][]byte{
|
||||
TagSCID: scfg.ID,
|
||||
|
||||
Reference in New Issue
Block a user