mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-05 21:27:35 +03:00
addressed reviewer comments
This commit is contained in:
parent
e859b12ad4
commit
361fd2d2b2
4 changed files with 36 additions and 86 deletions
|
@ -601,6 +601,8 @@ var _ = Describe("Client", func() {
|
||||||
Eventually(cl.versionNegotiated.Get).Should(BeTrue())
|
Eventually(cl.versionNegotiated.Get).Should(BeTrue())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Illustrates that adversary that injects a version negotiation packet
|
||||||
|
// with no supported versions can break a connection.
|
||||||
It("errors if no matching version is found", func() {
|
It("errors if no matching version is found", func() {
|
||||||
sess := NewMockQuicSession(mockCtrl)
|
sess := NewMockQuicSession(mockCtrl)
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
@ -692,24 +694,5 @@ var _ = Describe("Client", func() {
|
||||||
cl.handlePacket(composeVersionNegotiationPacket(connID, []protocol.VersionNumber{1234}))
|
cl.handlePacket(composeVersionNegotiationPacket(connID, []protocol.VersionNumber{1234}))
|
||||||
Expect(cl.version).To(Equal(ver))
|
Expect(cl.version).To(Equal(ver))
|
||||||
})
|
})
|
||||||
|
|
||||||
// Illustrates that adversary that injects a version negotiation packet
|
|
||||||
// with no supported versions can break a connection.
|
|
||||||
It("connection fails if no supported versions are found in version negotation packet", func() {
|
|
||||||
// Copy of existing test "errors if no matching version is found"
|
|
||||||
sess := NewMockQuicSession(mockCtrl)
|
|
||||||
done := make(chan struct{})
|
|
||||||
sess.EXPECT().destroy(gomock.Any()).Do(func(err error) {
|
|
||||||
defer GinkgoRecover()
|
|
||||||
Expect(err).To(HaveOccurred())
|
|
||||||
Expect(err.Error()).To(ContainSubstring("No compatible QUIC version found."))
|
|
||||||
close(done)
|
|
||||||
})
|
|
||||||
cl.session = sess
|
|
||||||
cl.config = &Config{Versions: protocol.SupportedVersions}
|
|
||||||
cl.handlePacket(composeVersionNegotiationPacket(connID, []protocol.VersionNumber{1337}))
|
|
||||||
Eventually(done).Should(BeClosed())
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -287,7 +287,7 @@ var _ = Describe("MITM test", func() {
|
||||||
// sendForgedVersionNegotiationPacket sends a fake VN packet with no supported versions
|
// sendForgedVersionNegotiationPacket sends a fake VN packet with no supported versions
|
||||||
// from serverConn to client's remoteAddr
|
// from serverConn to client's remoteAddr
|
||||||
// expects hdr from an Initial packet intercepted from client
|
// expects hdr from an Initial packet intercepted from client
|
||||||
sendForgedVersionNegotationPacket := func(serverConn net.PacketConn, remoteAddr net.Addr, hdr *wire.Header) {
|
sendForgedVersionNegotationPacket := func(conn net.PacketConn, remoteAddr net.Addr, hdr *wire.Header) {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
// Create fake version negotiation packet with no supported versions
|
// Create fake version negotiation packet with no supported versions
|
||||||
|
@ -295,24 +295,22 @@ var _ = Describe("MITM test", func() {
|
||||||
packet, _ := wire.ComposeVersionNegotiation(hdr.SrcConnectionID, hdr.DestConnectionID, versions)
|
packet, _ := wire.ComposeVersionNegotiation(hdr.SrcConnectionID, hdr.DestConnectionID, versions)
|
||||||
|
|
||||||
// Send the packet
|
// Send the packet
|
||||||
if _, err := serverConn.WriteTo(packet, remoteAddr); err != nil {
|
_, err := conn.WriteTo(packet, remoteAddr)
|
||||||
return
|
Expect(err).ToNot(HaveOccurred())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendForgedRetryPacket sends a fake Retry packet with a modified srcConnID
|
// sendForgedRetryPacket sends a fake Retry packet with a modified srcConnID
|
||||||
// from serverConn to client's remoteAddr
|
// from serverConn to client's remoteAddr
|
||||||
// expects hdr from an Initial packet intercepted from client
|
// expects hdr from an Initial packet intercepted from client
|
||||||
sendForgedRetryPacket := func(serverConn net.PacketConn, remoteAddr net.Addr, hdr *wire.Header) {
|
sendForgedRetryPacket := func(conn net.PacketConn, remoteAddr net.Addr, hdr *wire.Header) {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
var x byte = 0x12
|
var x byte = 0x12
|
||||||
fakeSrcConnID := protocol.ConnectionID{x, x, x, x, x, x, x, x}
|
fakeSrcConnID := protocol.ConnectionID{x, x, x, x, x, x, x, x}
|
||||||
retryPacket := testutils.ComposeRetryPacket(fakeSrcConnID, hdr.SrcConnectionID, hdr.DestConnectionID, []byte("token"), hdr.Version)
|
retryPacket := testutils.ComposeRetryPacket(fakeSrcConnID, hdr.SrcConnectionID, hdr.DestConnectionID, []byte("token"), hdr.Version)
|
||||||
|
|
||||||
if _, err := serverConn.WriteTo(retryPacket, remoteAddr); err != nil {
|
_, err := conn.WriteTo(retryPacket, remoteAddr)
|
||||||
return
|
Expect(err).ToNot(HaveOccurred())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a forged Initial packet with no frames to client
|
// Send a forged Initial packet with no frames to client
|
||||||
|
@ -320,10 +318,9 @@ var _ = Describe("MITM test", func() {
|
||||||
sendForgedInitialPacket := func(conn net.PacketConn, remoteAddr net.Addr, hdr *wire.Header) {
|
sendForgedInitialPacket := func(conn net.PacketConn, remoteAddr net.Addr, hdr *wire.Header) {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
initialPacket := testutils.ComposeInitialPacket(hdr.DestConnectionID, hdr.SrcConnectionID, hdr.Version, hdr.DestConnectionID, testutils.NoFrame)
|
initialPacket := testutils.ComposeInitialPacket(hdr.DestConnectionID, hdr.SrcConnectionID, hdr.Version, hdr.DestConnectionID, nil)
|
||||||
if _, err := conn.WriteTo(initialPacket, remoteAddr); err != nil {
|
_, err := conn.WriteTo(initialPacket, remoteAddr)
|
||||||
return
|
Expect(err).ToNot(HaveOccurred())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a forged Initial packet with ACK for random packet to client
|
// Send a forged Initial packet with ACK for random packet to client
|
||||||
|
@ -332,10 +329,10 @@ var _ = Describe("MITM test", func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
|
||||||
// Fake Initial with ACK for packet 2 (unsent)
|
// Fake Initial with ACK for packet 2 (unsent)
|
||||||
initialPacket := testutils.ComposeInitialPacket(hdr.DestConnectionID, hdr.SrcConnectionID, hdr.Version, hdr.DestConnectionID, testutils.AckFrame)
|
ackFrame := testutils.ComposeAckFrame(2, 2)
|
||||||
if _, err := conn.WriteTo(initialPacket, remoteAddr); err != nil {
|
initialPacket := testutils.ComposeInitialPacket(hdr.DestConnectionID, hdr.SrcConnectionID, hdr.Version, hdr.DestConnectionID, []wire.Frame{ackFrame})
|
||||||
return
|
_, err := conn.WriteTo(initialPacket, remoteAddr)
|
||||||
}
|
Expect(err).ToNot(HaveOccurred())
|
||||||
}
|
}
|
||||||
|
|
||||||
// runTestFail succeeds if an error occurs in dialing
|
// runTestFail succeeds if an error occurs in dialing
|
||||||
|
@ -367,7 +364,7 @@ var _ = Describe("MITM test", func() {
|
||||||
hdr, _, _, err := wire.ParsePacket(raw, connIDLen)
|
hdr, _, _, err := wire.ParsePacket(raw, connIDLen)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
if !(hdr.Type == protocol.PacketTypeInitial) {
|
if hdr.Type != protocol.PacketTypeInitial {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,7 +380,6 @@ var _ = Describe("MITM test", func() {
|
||||||
// TODO: determine behavior when server does not send Retry packets
|
// TODO: determine behavior when server does not send Retry packets
|
||||||
initialPacketIntercepted := false
|
initialPacketIntercepted := false
|
||||||
It("fails when a forged retry packet with modified srcConnID is sent to client", func() {
|
It("fails when a forged retry packet with modified srcConnID is sent to client", func() {
|
||||||
// serverConfig.AcceptToken = func(net.Addr, *quic.Token) bool { return true }
|
|
||||||
delayCb := func(dir quicproxy.Direction, raw []byte) time.Duration {
|
delayCb := func(dir quicproxy.Direction, raw []byte) time.Duration {
|
||||||
if dir == quicproxy.DirectionIncoming && !initialPacketIntercepted {
|
if dir == quicproxy.DirectionIncoming && !initialPacketIntercepted {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
@ -407,7 +403,6 @@ var _ = Describe("MITM test", func() {
|
||||||
// it has already accepted an initial.
|
// it has already accepted an initial.
|
||||||
// TODO: determine behavior when server does not send Retry packets
|
// TODO: determine behavior when server does not send Retry packets
|
||||||
It("fails when a forged initial packet is sent to client", func() {
|
It("fails when a forged initial packet is sent to client", func() {
|
||||||
// serverConfig.AcceptToken = func(net.Addr, *quic.Token) bool { return true }
|
|
||||||
delayCb := func(dir quicproxy.Direction, raw []byte) time.Duration {
|
delayCb := func(dir quicproxy.Direction, raw []byte) time.Duration {
|
||||||
if dir == quicproxy.DirectionIncoming {
|
if dir == quicproxy.DirectionIncoming {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
@ -415,7 +410,7 @@ var _ = Describe("MITM test", func() {
|
||||||
hdr, _, _, err := wire.ParsePacket(raw, connIDLen)
|
hdr, _, _, err := wire.ParsePacket(raw, connIDLen)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
if !(hdr.Type == protocol.PacketTypeInitial) {
|
if hdr.Type != protocol.PacketTypeInitial {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,7 +430,7 @@ var _ = Describe("MITM test", func() {
|
||||||
hdr, _, _, err := wire.ParsePacket(raw, connIDLen)
|
hdr, _, _, err := wire.ParsePacket(raw, connIDLen)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
if !(hdr.Type == protocol.PacketTypeInitial) {
|
if hdr.Type != protocol.PacketTypeInitial {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,29 +11,18 @@ import (
|
||||||
// Utilities for simulating packet injection and man-in-the-middle (MITM) attacker tests.
|
// Utilities for simulating packet injection and man-in-the-middle (MITM) attacker tests.
|
||||||
// Do not use for non-testing purposes.
|
// Do not use for non-testing purposes.
|
||||||
|
|
||||||
// CryptoFrameType types copied from unexported messageType in crypto_setup.go
|
// CryptoFrameType uses same types as messageType in crypto_setup.go
|
||||||
type CryptoFrameType uint8
|
type CryptoFrameType uint8
|
||||||
|
|
||||||
const (
|
// writePacket returns a new raw packet with the specified header and payload
|
||||||
//typeClientHello CryptoFrameType = 1
|
func writePacket(hdr *wire.ExtendedHeader, data []byte) []byte {
|
||||||
typeServerHello CryptoFrameType = 2
|
|
||||||
//typeNewSessionTicket CryptoFrameType = 4
|
|
||||||
//typeEncryptedExtensions CryptoFrameType = 8
|
|
||||||
//typeCertificate CryptoFrameType = 11
|
|
||||||
//typeCertificateRequest CryptoFrameType = 13
|
|
||||||
//typeCertificateVerify CryptoFrameType = 15
|
|
||||||
//typeFinished CryptoFrameType = 20
|
|
||||||
)
|
|
||||||
|
|
||||||
// getRawPacket returns a new raw packet with the specified header and payload
|
|
||||||
func getRawPacket(hdr *wire.ExtendedHeader, data []byte) []byte {
|
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
hdr.Write(buf, protocol.VersionTLS)
|
hdr.Write(buf, protocol.VersionTLS)
|
||||||
return append(buf.Bytes(), data...)
|
return append(buf.Bytes(), data...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// packRawPayload returns a new raw payload containing given frames
|
// packRawPayload returns a new raw payload containing given frames
|
||||||
func packRawPayload(version protocol.VersionNumber, frames ...wire.Frame) []byte {
|
func packRawPayload(version protocol.VersionNumber, frames []wire.Frame) []byte {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
for _, cf := range frames {
|
for _, cf := range frames {
|
||||||
cf.Write(buf, version)
|
cf.Write(buf, version)
|
||||||
|
@ -61,7 +50,7 @@ func ComposeConnCloseFrame() *wire.ConnectionCloseFrame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ComposeAckFrame returns a new Ack Frame that ACKs packet 0
|
// ComposeAckFrame returns a new Ack Frame that acknowledges all packets between smallest and largest
|
||||||
func ComposeAckFrame(smallest protocol.PacketNumber, largest protocol.PacketNumber) *wire.AckFrame {
|
func ComposeAckFrame(smallest protocol.PacketNumber, largest protocol.PacketNumber) *wire.AckFrame {
|
||||||
ackRange := wire.AckRange{
|
ackRange := wire.AckRange{
|
||||||
Smallest: smallest,
|
Smallest: smallest,
|
||||||
|
@ -73,36 +62,17 @@ func ComposeAckFrame(smallest protocol.PacketNumber, largest protocol.PacketNumb
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitialContents enumerates possible frames to include in forged Initial packets
|
|
||||||
type InitialContents int
|
|
||||||
|
|
||||||
const (
|
|
||||||
ServerHelloFrame InitialContents = iota + 1
|
|
||||||
AckFrame
|
|
||||||
ConnectionCloseFrame
|
|
||||||
NoFrame
|
|
||||||
)
|
|
||||||
|
|
||||||
// ComposeInitialPacket returns an Initial packet encrypted under key
|
// ComposeInitialPacket returns an Initial packet encrypted under key
|
||||||
// (the original destination connection ID)
|
// (the original destination connection ID) containing specified frames
|
||||||
// contains frame of specified type
|
func ComposeInitialPacket(srcConnID protocol.ConnectionID, destConnID protocol.ConnectionID, version protocol.VersionNumber, key protocol.ConnectionID, frames []wire.Frame) []byte {
|
||||||
func ComposeInitialPacket(srcConnID protocol.ConnectionID, destConnID protocol.ConnectionID, version protocol.VersionNumber, key protocol.ConnectionID, frameType InitialContents) []byte {
|
|
||||||
sealer, _, _ := handshake.NewInitialAEAD(key, protocol.PerspectiveServer)
|
sealer, _, _ := handshake.NewInitialAEAD(key, protocol.PerspectiveServer)
|
||||||
|
|
||||||
// compose payload
|
// compose payload
|
||||||
var payload []byte
|
var payload []byte
|
||||||
switch frameType {
|
if len(frames) == 0 {
|
||||||
case ServerHelloFrame:
|
|
||||||
cf := ComposeCryptoFrame(typeServerHello, 20)
|
|
||||||
payload = packRawPayload(version, cf)
|
|
||||||
case AckFrame:
|
|
||||||
ack := ComposeAckFrame(2, 2) // ack packet 2
|
|
||||||
payload = packRawPayload(version, ack)
|
|
||||||
case ConnectionCloseFrame:
|
|
||||||
ccf := ComposeConnCloseFrame()
|
|
||||||
payload = packRawPayload(version, ccf)
|
|
||||||
case NoFrame:
|
|
||||||
payload = make([]byte, protocol.MinInitialPacketSize)
|
payload = make([]byte, protocol.MinInitialPacketSize)
|
||||||
|
} else {
|
||||||
|
payload = packRawPayload(version, frames)
|
||||||
}
|
}
|
||||||
|
|
||||||
// compose Initial header
|
// compose Initial header
|
||||||
|
@ -122,7 +92,7 @@ func ComposeInitialPacket(srcConnID protocol.ConnectionID, destConnID protocol.C
|
||||||
PacketNumber: 0x0,
|
PacketNumber: 0x0,
|
||||||
}
|
}
|
||||||
|
|
||||||
raw := getRawPacket(hdr, payload)
|
raw := writePacket(hdr, payload)
|
||||||
|
|
||||||
// encrypt payload and header
|
// encrypt payload and header
|
||||||
payloadOffset := len(raw) - payloadSize
|
payloadOffset := len(raw) - payloadSize
|
||||||
|
@ -152,5 +122,5 @@ func ComposeRetryPacket(srcConnID protocol.ConnectionID, destConnID protocol.Con
|
||||||
Version: version,
|
Version: version,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return getRawPacket(hdr, nil)
|
return writePacket(hdr, nil)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1748,11 +1748,12 @@ var _ = Describe("Client Session", func() {
|
||||||
Expect(sess.handlePacketImpl(getPacket(hdr2, nil))).To(BeFalse())
|
Expect(sess.handlePacketImpl(getPacket(hdr2, nil))).To(BeFalse())
|
||||||
})
|
})
|
||||||
|
|
||||||
// Illustrates that an injected Initial with an ACK frame for an unsent causes
|
// Illustrates that an injected Initial with an ACK frame for an unsent packet causes
|
||||||
// the connection to immediately break down
|
// the connection to immediately break down
|
||||||
It("fails on Initial-level ACK for unsent packet", func() {
|
It("fails on Initial-level ACK for unsent packet", func() {
|
||||||
sessionRunner.EXPECT().Retire(gomock.Any())
|
sessionRunner.EXPECT().Retire(gomock.Any())
|
||||||
initialPacket := testutils.ComposeInitialPacket(sess.destConnID, sess.srcConnID, sess.version, sess.destConnID, testutils.AckFrame)
|
ackFrame := testutils.ComposeAckFrame(0, 0)
|
||||||
|
initialPacket := testutils.ComposeInitialPacket(sess.destConnID, sess.srcConnID, sess.version, sess.destConnID, []wire.Frame{ackFrame})
|
||||||
Expect(sess.handlePacketImpl(wrapPacket(initialPacket))).To(BeFalse())
|
Expect(sess.handlePacketImpl(wrapPacket(initialPacket))).To(BeFalse())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1760,7 +1761,8 @@ var _ = Describe("Client Session", func() {
|
||||||
// the connection to immediately break down
|
// the connection to immediately break down
|
||||||
It("fails on Initial-level CONNECTION_CLOSE frame", func() {
|
It("fails on Initial-level CONNECTION_CLOSE frame", func() {
|
||||||
sessionRunner.EXPECT().Remove(gomock.Any())
|
sessionRunner.EXPECT().Remove(gomock.Any())
|
||||||
initialPacket := testutils.ComposeInitialPacket(sess.destConnID, sess.srcConnID, sess.version, sess.destConnID, testutils.ConnectionCloseFrame)
|
connCloseFrame := testutils.ComposeConnCloseFrame()
|
||||||
|
initialPacket := testutils.ComposeInitialPacket(sess.destConnID, sess.srcConnID, sess.version, sess.destConnID, []wire.Frame{connCloseFrame})
|
||||||
Expect(sess.handlePacketImpl(wrapPacket(initialPacket))).To(BeTrue())
|
Expect(sess.handlePacketImpl(wrapPacket(initialPacket))).To(BeTrue())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1773,7 +1775,7 @@ var _ = Describe("Client Session", func() {
|
||||||
packer.EXPECT().ChangeDestConnectionID(newSrcConnID)
|
packer.EXPECT().ChangeDestConnectionID(newSrcConnID)
|
||||||
|
|
||||||
sess.handlePacketImpl(wrapPacket(testutils.ComposeRetryPacket(newSrcConnID, sess.destConnID, sess.destConnID, []byte("foobar"), sess.version)))
|
sess.handlePacketImpl(wrapPacket(testutils.ComposeRetryPacket(newSrcConnID, sess.destConnID, sess.destConnID, []byte("foobar"), sess.version)))
|
||||||
initialPacket := testutils.ComposeInitialPacket(sess.destConnID, sess.srcConnID, sess.version, sess.destConnID, testutils.NoFrame)
|
initialPacket := testutils.ComposeInitialPacket(sess.destConnID, sess.srcConnID, sess.version, sess.destConnID, nil)
|
||||||
Expect(sess.handlePacketImpl(wrapPacket(initialPacket))).To(BeFalse())
|
Expect(sess.handlePacketImpl(wrapPacket(initialPacket))).To(BeFalse())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue