forked from quic-go/quic-go
Merge pull request #449 from lucas-clemente/new-fix-391
immediately switch to forward-secure crypto after sending the SHLO
This commit is contained in:
@@ -10,9 +10,10 @@ import (
|
||||
// A Packet is a packet
|
||||
// +gen linkedlist
|
||||
type Packet struct {
|
||||
PacketNumber protocol.PacketNumber
|
||||
Frames []frames.Frame
|
||||
Length protocol.ByteCount
|
||||
PacketNumber protocol.PacketNumber
|
||||
Frames []frames.Frame
|
||||
Length protocol.ByteCount
|
||||
EncryptionLevel protocol.EncryptionLevel
|
||||
|
||||
MissingReports uint8
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ func (c *linkedConnection) Close() error { return nil }
|
||||
|
||||
func setAEAD(cs handshake.CryptoSetup, aead crypto.AEAD) {
|
||||
*(*bool)(unsafe.Pointer(reflect.ValueOf(cs).Elem().FieldByName("receivedForwardSecurePacket").UnsafeAddr())) = true
|
||||
*(*bool)(unsafe.Pointer(reflect.ValueOf(cs).Elem().FieldByName("sentSHLO").UnsafeAddr())) = true
|
||||
*(*crypto.AEAD)(unsafe.Pointer(reflect.ValueOf(cs).Elem().FieldByName("forwardSecureAEAD").UnsafeAddr())) = aead
|
||||
}
|
||||
|
||||
|
||||
@@ -312,7 +312,26 @@ func (h *cryptoSetupClient) Seal(dst, src []byte, packetNumber protocol.PacketNu
|
||||
return (&crypto.NullAEAD{}).Seal(dst, src, packetNumber, associatedData), protocol.EncryptionUnencrypted
|
||||
}
|
||||
|
||||
func (h *cryptoSetupClient) DiversificationNonce() []byte {
|
||||
func (h *cryptoSetupClient) SealWith(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte, forceEncryptionLevel protocol.EncryptionLevel) ([]byte, protocol.EncryptionLevel, error) {
|
||||
switch forceEncryptionLevel {
|
||||
case protocol.EncryptionUnencrypted:
|
||||
return (&crypto.NullAEAD{}).Seal(dst, src, packetNumber, associatedData), protocol.EncryptionUnencrypted, nil
|
||||
case protocol.EncryptionSecure:
|
||||
if h.secureAEAD == nil {
|
||||
return nil, protocol.EncryptionUnspecified, errors.New("CryptoSetupClient: no secureAEAD")
|
||||
}
|
||||
return h.secureAEAD.Seal(dst, src, packetNumber, associatedData), protocol.EncryptionSecure, nil
|
||||
case protocol.EncryptionForwardSecure:
|
||||
if h.forwardSecureAEAD == nil {
|
||||
return nil, protocol.EncryptionUnspecified, errors.New("CryptoSetupClient: no forwardSecureAEAD")
|
||||
}
|
||||
return h.forwardSecureAEAD.Seal(dst, src, packetNumber, associatedData), protocol.EncryptionForwardSecure, nil
|
||||
}
|
||||
|
||||
return nil, protocol.EncryptionUnspecified, errors.New("no encryption level specified")
|
||||
}
|
||||
|
||||
func (h *cryptoSetupClient) DiversificationNonce(bool) []byte {
|
||||
panic("not needed for cryptoSetupClient")
|
||||
}
|
||||
|
||||
|
||||
@@ -744,6 +744,48 @@ var _ = Describe("Crypto setup", func() {
|
||||
Expect(enc).To(Equal(protocol.EncryptionForwardSecure))
|
||||
})
|
||||
})
|
||||
|
||||
Context("forcing encryption levels", func() {
|
||||
It("forces null encryption", func() {
|
||||
d, enc, err := cs.SealWith(nil, []byte("foobar"), 0, []byte{}, protocol.EncryptionUnencrypted)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(d).To(Equal(foobarFNVSigned))
|
||||
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
|
||||
})
|
||||
|
||||
It("forces initial encryption", func() {
|
||||
doCompleteREJ()
|
||||
d, enc, err := cs.SealWith(nil, []byte("foobar"), 0, []byte{}, protocol.EncryptionSecure)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(d).To(Equal([]byte("foobar normal sec")))
|
||||
Expect(enc).To(Equal(protocol.EncryptionSecure))
|
||||
})
|
||||
|
||||
It("errors of no AEAD for initial encryption is available", func() {
|
||||
_, enc, err := cs.SealWith(nil, []byte("foobar"), 0, []byte{}, protocol.EncryptionSecure)
|
||||
Expect(err).To(MatchError("CryptoSetupClient: no secureAEAD"))
|
||||
Expect(enc).To(Equal(protocol.EncryptionUnspecified))
|
||||
})
|
||||
|
||||
It("forces forward-secure encryption", func() {
|
||||
doSHLO()
|
||||
d, enc, err := cs.SealWith(nil, []byte("foobar"), 0, []byte{}, protocol.EncryptionForwardSecure)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(d).To(Equal([]byte("foobar forward sec")))
|
||||
Expect(enc).To(Equal(protocol.EncryptionForwardSecure))
|
||||
})
|
||||
|
||||
It("errors of no AEAD for forward-secure encryption is available", func() {
|
||||
_, enc, err := cs.SealWith(nil, []byte("foobar"), 0, []byte{}, protocol.EncryptionForwardSecure)
|
||||
Expect(err).To(MatchError("CryptoSetupClient: no forwardSecureAEAD"))
|
||||
Expect(enc).To(Equal(protocol.EncryptionUnspecified))
|
||||
})
|
||||
|
||||
It("errors if no encryption level is specified", func() {
|
||||
_, _, err := cs.SealWith(nil, []byte("foobar"), 0, []byte{}, protocol.EncryptionUnspecified)
|
||||
Expect(err).To(MatchError("no encryption level specified"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("Diversification Nonces", func() {
|
||||
|
||||
@@ -7,10 +7,11 @@ type CryptoSetup interface {
|
||||
HandleCryptoStream() error
|
||||
Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error)
|
||||
Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel)
|
||||
SealWith(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte, forceEncryptionLevel protocol.EncryptionLevel) ([]byte, protocol.EncryptionLevel, error)
|
||||
LockForSealing()
|
||||
UnlockForSealing()
|
||||
HandshakeComplete() bool
|
||||
// TODO: clean up this interface
|
||||
DiversificationNonce() []byte // only needed for cryptoSetupServer
|
||||
SetDiversificationNonce([]byte) error // only needed for cryptoSetupClient
|
||||
DiversificationNonce(force bool) []byte // only needed for cryptoSetupServer
|
||||
SetDiversificationNonce([]byte) error // only needed for cryptoSetupClient
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
@@ -30,6 +31,7 @@ type cryptoSetupServer struct {
|
||||
secureAEAD crypto.AEAD
|
||||
forwardSecureAEAD crypto.AEAD
|
||||
receivedForwardSecurePacket bool
|
||||
sentSHLO bool
|
||||
receivedSecurePacket bool
|
||||
aeadChanged chan protocol.EncryptionLevel
|
||||
|
||||
@@ -186,15 +188,37 @@ func (h *cryptoSetupServer) Open(dst, src []byte, packetNumber protocol.PacketNu
|
||||
|
||||
// Seal a message, call LockForSealing() before!
|
||||
func (h *cryptoSetupServer) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel) {
|
||||
if h.receivedForwardSecurePacket {
|
||||
if h.forwardSecureAEAD != nil && h.sentSHLO {
|
||||
return h.forwardSecureAEAD.Seal(dst, src, packetNumber, associatedData), protocol.EncryptionForwardSecure
|
||||
} else if h.secureAEAD != nil {
|
||||
// secureAEAD and forwardSecureAEAD are created at the same time (when receiving the CHLO)
|
||||
// make sure that the SHLO isn't sent forward-secure
|
||||
h.sentSHLO = true
|
||||
return h.secureAEAD.Seal(dst, src, packetNumber, associatedData), protocol.EncryptionSecure
|
||||
} else {
|
||||
return (&crypto.NullAEAD{}).Seal(dst, src, packetNumber, associatedData), protocol.EncryptionUnencrypted
|
||||
}
|
||||
}
|
||||
|
||||
func (h *cryptoSetupServer) SealWith(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte, forceEncryptionLevel protocol.EncryptionLevel) ([]byte, protocol.EncryptionLevel, error) {
|
||||
switch forceEncryptionLevel {
|
||||
case protocol.EncryptionUnencrypted:
|
||||
return (&crypto.NullAEAD{}).Seal(dst, src, packetNumber, associatedData), protocol.EncryptionUnencrypted, nil
|
||||
case protocol.EncryptionSecure:
|
||||
if h.secureAEAD == nil {
|
||||
return nil, protocol.EncryptionUnspecified, errors.New("CryptoSetupServer: no secureAEAD")
|
||||
}
|
||||
return h.secureAEAD.Seal(dst, src, packetNumber, associatedData), protocol.EncryptionSecure, nil
|
||||
case protocol.EncryptionForwardSecure:
|
||||
if h.forwardSecureAEAD == nil {
|
||||
return nil, protocol.EncryptionUnspecified, errors.New("CryptoSetupServer: no forwardSecureAEAD")
|
||||
}
|
||||
return h.forwardSecureAEAD.Seal(dst, src, packetNumber, associatedData), protocol.EncryptionForwardSecure, nil
|
||||
}
|
||||
|
||||
return nil, protocol.EncryptionUnspecified, errors.New("no encryption level specified")
|
||||
}
|
||||
|
||||
func (h *cryptoSetupServer) isInchoateCHLO(cryptoData map[Tag][]byte, cert []byte) bool {
|
||||
if _, ok := cryptoData[TagPUBS]; !ok {
|
||||
return true
|
||||
@@ -314,6 +338,8 @@ func (h *cryptoSetupServer) handleCHLO(sni string, data []byte, cryptoData map[T
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h.aeadChanged <- protocol.EncryptionSecure
|
||||
|
||||
// Generate a new curve instance to derive the forward secure key
|
||||
var fsNonce bytes.Buffer
|
||||
fsNonce.Write(clientNonce)
|
||||
@@ -353,6 +379,7 @@ func (h *cryptoSetupServer) handleCHLO(sni string, data []byte, cryptoData map[T
|
||||
replyMap[TagSNO] = serverNonce
|
||||
replyMap[TagVER] = protocol.SupportedVersionsAsTags
|
||||
|
||||
// note that the SHLO *has* to fit into one packet
|
||||
var reply bytes.Buffer
|
||||
WriteHandshakeMessage(&reply, TagSHLO, replyMap)
|
||||
utils.Debugf("Sending SHLO:\n%s", printHandshakeMessage(replyMap))
|
||||
@@ -363,11 +390,11 @@ func (h *cryptoSetupServer) handleCHLO(sni string, data []byte, cryptoData map[T
|
||||
}
|
||||
|
||||
// DiversificationNonce returns a diversification nonce if required in the next packet to be Seal'ed. See LockForSealing()!
|
||||
func (h *cryptoSetupServer) DiversificationNonce() []byte {
|
||||
if h.receivedForwardSecurePacket || h.secureAEAD == nil {
|
||||
return nil
|
||||
func (h *cryptoSetupServer) DiversificationNonce(force bool) []byte {
|
||||
if force || (h.secureAEAD != nil && !h.sentSHLO) {
|
||||
return h.diversificationNonce
|
||||
}
|
||||
return h.diversificationNonce
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *cryptoSetupServer) SetDiversificationNonce(data []byte) error {
|
||||
|
||||
@@ -185,23 +185,28 @@ var _ = Describe("Crypto setup", func() {
|
||||
cs.secureAEAD = &mockAEAD{}
|
||||
cs.receivedForwardSecurePacket = false
|
||||
|
||||
Expect(cs.DiversificationNonce()).To(BeEmpty())
|
||||
Expect(cs.DiversificationNonce(false)).To(BeEmpty())
|
||||
// Div nonce is created after CHLO
|
||||
cs.handleCHLO("", nil, map[Tag][]byte{TagNONC: nonce32})
|
||||
})
|
||||
|
||||
It("returns diversification nonces", func() {
|
||||
Expect(cs.DiversificationNonce()).To(HaveLen(32))
|
||||
Expect(cs.DiversificationNonce(false)).To(HaveLen(32))
|
||||
})
|
||||
|
||||
It("does not return nonce for FS packets", func() {
|
||||
cs.receivedForwardSecurePacket = true
|
||||
Expect(cs.DiversificationNonce()).To(BeEmpty())
|
||||
It("does not return nonce after sending the SHLO", func() {
|
||||
cs.sentSHLO = true
|
||||
Expect(cs.DiversificationNonce(false)).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("returns a nonce for a retransmission, even after sending the SHLO", func() {
|
||||
cs.sentSHLO = true
|
||||
Expect(cs.DiversificationNonce(true)).To(HaveLen(32))
|
||||
})
|
||||
|
||||
It("does not return nonce for unencrypted packets", func() {
|
||||
cs.secureAEAD = nil
|
||||
Expect(cs.DiversificationNonce()).To(BeEmpty())
|
||||
Expect(cs.DiversificationNonce(false)).To(BeEmpty())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -336,7 +341,11 @@ var _ = Describe("Crypto setup", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(stream.dataWritten.Bytes()).To(HavePrefix("SHLO"))
|
||||
Expect(stream.dataWritten.Bytes()).ToNot(ContainSubstring("REJ"))
|
||||
Expect(aeadChanged).To(Receive())
|
||||
var encLevel protocol.EncryptionLevel
|
||||
Expect(aeadChanged).To(Receive(&encLevel))
|
||||
Expect(encLevel).To(Equal(protocol.EncryptionSecure))
|
||||
Expect(aeadChanged).To(Receive(&encLevel))
|
||||
Expect(encLevel).To(Equal(protocol.EncryptionForwardSecure))
|
||||
})
|
||||
|
||||
It("recognizes inchoate CHLOs missing SCID", func() {
|
||||
@@ -629,15 +638,6 @@ var _ = Describe("Crypto setup", func() {
|
||||
Expect(d).To(Equal([]byte("decrypted")))
|
||||
})
|
||||
|
||||
It("is not used after receiving forward secure packet", func() {
|
||||
doCHLO()
|
||||
_, _, err := cs.Open(nil, []byte("forward secure encrypted"), 0, []byte{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
d, enc := cs.Seal(nil, []byte("foobar"), 0, []byte{})
|
||||
Expect(d).To(Equal([]byte("foobar forward sec")))
|
||||
Expect(enc).To(Equal(protocol.EncryptionForwardSecure))
|
||||
})
|
||||
|
||||
It("is not accepted after receiving forward secure packet", func() {
|
||||
doCHLO()
|
||||
_, _, err := cs.Open(nil, []byte("forward secure encrypted"), 0, []byte{})
|
||||
@@ -649,15 +649,67 @@ var _ = Describe("Crypto setup", func() {
|
||||
})
|
||||
|
||||
Context("forward secure encryption", func() {
|
||||
It("is used after receiving forward secure packet", func() {
|
||||
It("is used after sending out one packet with initial encryption", func() {
|
||||
doCHLO()
|
||||
_, enc, err := cs.Open(nil, []byte("forward secure encrypted"), 0, []byte{})
|
||||
Expect(enc).To(Equal(protocol.EncryptionForwardSecure))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
_, enc := cs.Seal(nil, []byte("SHLO"), 0, []byte{})
|
||||
Expect(enc).To(Equal(protocol.EncryptionSecure))
|
||||
d, enc := cs.Seal(nil, []byte("foobar"), 0, []byte{})
|
||||
Expect(d).To(Equal([]byte("foobar forward sec")))
|
||||
Expect(enc).To(Equal(protocol.EncryptionForwardSecure))
|
||||
})
|
||||
|
||||
It("regards the handshake as complete once it receives a forward encrypted packet", func() {
|
||||
doCHLO()
|
||||
_, enc := cs.Seal(nil, []byte("SHLO"), 0, []byte{})
|
||||
Expect(enc).To(Equal(protocol.EncryptionSecure))
|
||||
_, enc = cs.Seal(nil, []byte("foobar"), 0, []byte{})
|
||||
Expect(enc).To(Equal(protocol.EncryptionForwardSecure))
|
||||
Expect(cs.HandshakeComplete()).To(BeFalse())
|
||||
cs.receivedForwardSecurePacket = true
|
||||
Expect(cs.HandshakeComplete()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("forcing encryption levels", func() {
|
||||
It("forces null encryption", func() {
|
||||
d, enc, err := cs.SealWith(nil, []byte("foobar"), 0, []byte{}, protocol.EncryptionUnencrypted)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(d).To(Equal(foobarFNVSigned))
|
||||
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
|
||||
})
|
||||
|
||||
It("forces initial encryption", func() {
|
||||
doCHLO()
|
||||
d, enc, err := cs.SealWith(nil, []byte("foobar"), 0, []byte{}, protocol.EncryptionSecure)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(d).To(Equal([]byte("foobar normal sec")))
|
||||
Expect(enc).To(Equal(protocol.EncryptionSecure))
|
||||
})
|
||||
|
||||
It("errors of no AEAD for initial encryption is available", func() {
|
||||
_, enc, err := cs.SealWith(nil, []byte("foobar"), 0, []byte{}, protocol.EncryptionSecure)
|
||||
Expect(err).To(MatchError("CryptoSetupServer: no secureAEAD"))
|
||||
Expect(enc).To(Equal(protocol.EncryptionUnspecified))
|
||||
})
|
||||
|
||||
It("forces forward-secure encryption", func() {
|
||||
doCHLO()
|
||||
d, enc, err := cs.SealWith(nil, []byte("foobar"), 0, []byte{}, protocol.EncryptionForwardSecure)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(d).To(Equal([]byte("foobar forward sec")))
|
||||
Expect(enc).To(Equal(protocol.EncryptionForwardSecure))
|
||||
})
|
||||
|
||||
It("errors of no AEAD for forward-secure encryption is available", func() {
|
||||
_, enc, err := cs.SealWith(nil, []byte("foobar"), 0, []byte{}, protocol.EncryptionForwardSecure)
|
||||
Expect(err).To(MatchError("CryptoSetupServer: no forwardSecureAEAD"))
|
||||
Expect(enc).To(Equal(protocol.EncryptionUnspecified))
|
||||
})
|
||||
|
||||
It("errors if no encryption level is specified", func() {
|
||||
_, _, err := cs.SealWith(nil, []byte("foobar"), 0, []byte{}, protocol.EncryptionUnspecified)
|
||||
Expect(err).To(MatchError("no encryption level specified"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/ackhandler"
|
||||
"github.com/lucas-clemente/quic-go/frames"
|
||||
"github.com/lucas-clemente/quic-go/handshake"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
@@ -23,6 +24,8 @@ type packetPacker struct {
|
||||
perspective protocol.Perspective
|
||||
version protocol.VersionNumber
|
||||
cryptoSetup handshake.CryptoSetup
|
||||
// as long as packets are not sent with forward-secure encryption, we limit the MaxPacketSize such that they can be retransmitted as a whole
|
||||
isForwardSecure bool
|
||||
|
||||
packetNumberGenerator *packetNumberGenerator
|
||||
|
||||
@@ -49,7 +52,19 @@ func (p *packetPacker) PackConnectionClose(ccf *frames.ConnectionCloseFrame, lea
|
||||
// in case the connection is closed, all queued control frames aren't of any use anymore
|
||||
// discard them and queue the ConnectionCloseFrame
|
||||
p.controlFrames = []frames.Frame{ccf}
|
||||
return p.packPacket(nil, leastUnacked)
|
||||
return p.packPacket(nil, leastUnacked, nil)
|
||||
}
|
||||
|
||||
// RetransmitNonForwardSecurePacket retransmits a handshake packet, that was sent with less than forward-secure encryption
|
||||
func (p *packetPacker) RetransmitNonForwardSecurePacket(stopWaitingFrame *frames.StopWaitingFrame, packet *ackhandler.Packet) (*packedPacket, error) {
|
||||
if packet.EncryptionLevel == protocol.EncryptionForwardSecure {
|
||||
return nil, errors.New("PacketPacker BUG: forward-secure encrypted handshake packets don't need special treatment")
|
||||
}
|
||||
if stopWaitingFrame == nil {
|
||||
return nil, errors.New("PacketPacker BUG: Handshake retransmissions must contain a StopWaitingFrame")
|
||||
}
|
||||
|
||||
return p.packPacket(stopWaitingFrame, 0, packet)
|
||||
}
|
||||
|
||||
// PackPacket packs a new packet
|
||||
@@ -57,10 +72,12 @@ func (p *packetPacker) PackConnectionClose(ccf *frames.ConnectionCloseFrame, lea
|
||||
// the other controlFrames are sent in the next packet, but might be queued and sent in the next packet if the packet would overflow MaxPacketSize otherwise
|
||||
func (p *packetPacker) PackPacket(stopWaitingFrame *frames.StopWaitingFrame, controlFrames []frames.Frame, leastUnacked protocol.PacketNumber) (*packedPacket, error) {
|
||||
p.controlFrames = append(p.controlFrames, controlFrames...)
|
||||
return p.packPacket(stopWaitingFrame, leastUnacked)
|
||||
return p.packPacket(stopWaitingFrame, leastUnacked, nil)
|
||||
}
|
||||
|
||||
func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, leastUnacked protocol.PacketNumber) (*packedPacket, error) {
|
||||
func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, leastUnacked protocol.PacketNumber, packetToRetransmit *ackhandler.Packet) (*packedPacket, error) {
|
||||
// packetToRetransmit is only set for handshake retransmissions
|
||||
isHandshakeRetransmission := (packetToRetransmit != nil)
|
||||
// cryptoSetup needs to be locked here, so that the AEADs are not changed between
|
||||
// calling DiversificationNonce() and Seal().
|
||||
p.cryptoSetup.LockForSealing()
|
||||
@@ -76,7 +93,8 @@ func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, lea
|
||||
}
|
||||
|
||||
if p.perspective == protocol.PerspectiveServer {
|
||||
responsePublicHeader.DiversificationNonce = p.cryptoSetup.DiversificationNonce()
|
||||
force := isHandshakeRetransmission && (packetToRetransmit.EncryptionLevel == protocol.EncryptionSecure)
|
||||
responsePublicHeader.DiversificationNonce = p.cryptoSetup.DiversificationNonce(force)
|
||||
}
|
||||
|
||||
if p.perspective == protocol.PerspectiveClient && !p.cryptoSetup.HandshakeComplete() {
|
||||
@@ -101,10 +119,26 @@ func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, lea
|
||||
}
|
||||
|
||||
var payloadFrames []frames.Frame
|
||||
if isConnectionClose {
|
||||
if isHandshakeRetransmission {
|
||||
payloadFrames = append(payloadFrames, stopWaitingFrame)
|
||||
// don't retransmit Acks and StopWaitings
|
||||
for _, f := range packetToRetransmit.Frames {
|
||||
switch f.(type) {
|
||||
case *frames.AckFrame:
|
||||
continue
|
||||
case *frames.StopWaitingFrame:
|
||||
continue
|
||||
}
|
||||
payloadFrames = append(payloadFrames, f)
|
||||
}
|
||||
} else if isConnectionClose {
|
||||
payloadFrames = []frames.Frame{p.controlFrames[0]}
|
||||
} else {
|
||||
payloadFrames, err = p.composeNextPacket(stopWaitingFrame, publicHeaderLength)
|
||||
maxSize := protocol.MaxFrameAndPublicHeaderSize - publicHeaderLength
|
||||
if !p.isForwardSecure {
|
||||
maxSize -= protocol.NonForwardSecurePacketSizeReduction
|
||||
}
|
||||
payloadFrames, err = p.composeNextPacket(stopWaitingFrame, maxSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -133,7 +167,7 @@ func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, lea
|
||||
if sf, ok := frame.(*frames.StreamFrame); ok && sf.StreamID != 1 {
|
||||
hasNonCryptoStreamData = true
|
||||
}
|
||||
err := frame.Write(buffer, p.version)
|
||||
err = frame.Write(buffer, p.version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -144,7 +178,16 @@ func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, lea
|
||||
}
|
||||
|
||||
raw = raw[0:buffer.Len()]
|
||||
_, encryptionLevel := p.cryptoSetup.Seal(raw[payloadStartIndex:payloadStartIndex], raw[payloadStartIndex:], currentPacketNumber, raw[:payloadStartIndex])
|
||||
var encryptionLevel protocol.EncryptionLevel
|
||||
if isHandshakeRetransmission {
|
||||
var err error
|
||||
_, encryptionLevel, err = p.cryptoSetup.SealWith(raw[payloadStartIndex:payloadStartIndex], raw[payloadStartIndex:], currentPacketNumber, raw[:payloadStartIndex], packetToRetransmit.EncryptionLevel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
_, encryptionLevel = p.cryptoSetup.Seal(raw[payloadStartIndex:payloadStartIndex], raw[payloadStartIndex:], currentPacketNumber, raw[:payloadStartIndex])
|
||||
}
|
||||
raw = raw[0 : buffer.Len()+12]
|
||||
|
||||
if hasNonCryptoStreamData && encryptionLevel <= protocol.EncryptionUnencrypted {
|
||||
@@ -164,12 +207,10 @@ func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, lea
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *packetPacker) composeNextPacket(stopWaitingFrame *frames.StopWaitingFrame, publicHeaderLength protocol.ByteCount) ([]frames.Frame, error) {
|
||||
func (p *packetPacker) composeNextPacket(stopWaitingFrame *frames.StopWaitingFrame, maxFrameSize protocol.ByteCount) ([]frames.Frame, error) {
|
||||
var payloadLength protocol.ByteCount
|
||||
var payloadFrames []frames.Frame
|
||||
|
||||
maxFrameSize := protocol.MaxFrameAndPublicHeaderSize - publicHeaderLength
|
||||
|
||||
if stopWaitingFrame != nil {
|
||||
payloadFrames = append(payloadFrames, stopWaitingFrame)
|
||||
minLength, err := stopWaitingFrame.MinLength(p.version)
|
||||
@@ -219,3 +260,7 @@ func (p *packetPacker) composeNextPacket(stopWaitingFrame *frames.StopWaitingFra
|
||||
func (p *packetPacker) QueueControlFrameForNextPacket(f frames.Frame) {
|
||||
p.controlFrames = append(p.controlFrames, f)
|
||||
}
|
||||
|
||||
func (p *packetPacker) SetForwardSecure() {
|
||||
p.isForwardSecure = true
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package quic
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/ackhandler"
|
||||
"github.com/lucas-clemente/quic-go/frames"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
@@ -11,9 +12,10 @@ import (
|
||||
)
|
||||
|
||||
type mockCryptoSetup struct {
|
||||
diversificationNonce []byte
|
||||
handshakeComplete bool
|
||||
encLevelSeal protocol.EncryptionLevel
|
||||
divNonce []byte
|
||||
forcedDivNonce bool
|
||||
handshakeComplete bool
|
||||
encLevelSeal protocol.EncryptionLevel
|
||||
}
|
||||
|
||||
func (m *mockCryptoSetup) HandleCryptoStream() error { return nil }
|
||||
@@ -24,16 +26,23 @@ func (m *mockCryptoSetup) Open(dst, src []byte, packetNumber protocol.PacketNumb
|
||||
func (m *mockCryptoSetup) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel) {
|
||||
return append(src, bytes.Repeat([]byte{0}, 12)...), m.encLevelSeal
|
||||
}
|
||||
func (m *mockCryptoSetup) LockForSealing() {}
|
||||
func (m *mockCryptoSetup) UnlockForSealing() {}
|
||||
func (m *mockCryptoSetup) HandshakeComplete() bool { return m.handshakeComplete }
|
||||
func (m *mockCryptoSetup) DiversificationNonce() []byte { return m.diversificationNonce }
|
||||
func (m *mockCryptoSetup) SealWith(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte, encLevel protocol.EncryptionLevel) ([]byte, protocol.EncryptionLevel, error) {
|
||||
return append(src, bytes.Repeat([]byte{0}, 12)...), encLevel, nil
|
||||
}
|
||||
func (m *mockCryptoSetup) LockForSealing() {}
|
||||
func (m *mockCryptoSetup) UnlockForSealing() {}
|
||||
func (m *mockCryptoSetup) HandshakeComplete() bool { return m.handshakeComplete }
|
||||
func (m *mockCryptoSetup) DiversificationNonce(force bool) []byte {
|
||||
m.forcedDivNonce = force
|
||||
return m.divNonce
|
||||
}
|
||||
func (m *mockCryptoSetup) SetDiversificationNonce([]byte) error { panic("not implemented") }
|
||||
|
||||
var _ = Describe("Packet packer", func() {
|
||||
var (
|
||||
packer *packetPacker
|
||||
publicHeaderLen protocol.ByteCount
|
||||
maxFrameSize protocol.ByteCount
|
||||
streamFramer *streamFramer
|
||||
)
|
||||
|
||||
@@ -49,12 +58,15 @@ var _ = Describe("Packet packer", func() {
|
||||
packer = &packetPacker{
|
||||
cryptoSetup: &mockCryptoSetup{encLevelSeal: protocol.EncryptionForwardSecure},
|
||||
connectionParameters: cpm,
|
||||
connectionID: 0x1337,
|
||||
packetNumberGenerator: newPacketNumberGenerator(protocol.SkipPacketAveragePeriodLength),
|
||||
streamFramer: streamFramer,
|
||||
perspective: protocol.PerspectiveServer,
|
||||
}
|
||||
publicHeaderLen = 1 + 8 + 2 // 1 flag byte, 8 connection ID, 2 packet number
|
||||
maxFrameSize = protocol.MaxFrameAndPublicHeaderSize - publicHeaderLen
|
||||
packer.version = protocol.Version34
|
||||
packer.isForwardSecure = true
|
||||
})
|
||||
|
||||
It("returns nil when no packet is queued", func() {
|
||||
@@ -92,7 +104,7 @@ var _ = Describe("Packet packer", func() {
|
||||
|
||||
It("includes a diversification nonce, when acting as a server", func() {
|
||||
nonce := bytes.Repeat([]byte{'e'}, 32)
|
||||
packer.cryptoSetup.(*mockCryptoSetup).diversificationNonce = nonce
|
||||
packer.cryptoSetup.(*mockCryptoSetup).divNonce = nonce
|
||||
f := &frames.StreamFrame{
|
||||
StreamID: 5,
|
||||
Data: []byte{0xDE, 0xCA, 0xFB, 0xAD},
|
||||
@@ -219,10 +231,10 @@ var _ = Describe("Packet packer", func() {
|
||||
controlFrames = append(controlFrames, f)
|
||||
}
|
||||
packer.controlFrames = controlFrames
|
||||
payloadFrames, err := packer.composeNextPacket(nil, publicHeaderLen)
|
||||
payloadFrames, err := packer.composeNextPacket(nil, maxFrameSize)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(payloadFrames).To(HaveLen(maxFramesPerPacket))
|
||||
payloadFrames, err = packer.composeNextPacket(nil, publicHeaderLen)
|
||||
payloadFrames, err = packer.composeNextPacket(nil, maxFrameSize)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(payloadFrames).To(BeEmpty())
|
||||
})
|
||||
@@ -238,10 +250,10 @@ var _ = Describe("Packet packer", func() {
|
||||
controlFrames = append(controlFrames, blockedFrame)
|
||||
}
|
||||
packer.controlFrames = controlFrames
|
||||
payloadFrames, err := packer.composeNextPacket(nil, publicHeaderLen)
|
||||
payloadFrames, err := packer.composeNextPacket(nil, maxFrameSize)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(payloadFrames).To(HaveLen(maxFramesPerPacket))
|
||||
payloadFrames, err = packer.composeNextPacket(nil, publicHeaderLen)
|
||||
payloadFrames, err = packer.composeNextPacket(nil, maxFrameSize)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(payloadFrames).To(HaveLen(10))
|
||||
})
|
||||
@@ -272,14 +284,14 @@ var _ = Describe("Packet packer", func() {
|
||||
DataLenPresent: false,
|
||||
}
|
||||
minLength, _ := f.MinLength(0)
|
||||
maxStreamFrameDataLen := protocol.MaxFrameAndPublicHeaderSize - publicHeaderLen - minLength
|
||||
maxStreamFrameDataLen := maxFrameSize - minLength
|
||||
f.Data = bytes.Repeat([]byte{'f'}, int(maxStreamFrameDataLen))
|
||||
streamFramer.AddFrameForRetransmission(f)
|
||||
payloadFrames, err := packer.composeNextPacket(nil, publicHeaderLen)
|
||||
payloadFrames, err := packer.composeNextPacket(nil, maxFrameSize)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(payloadFrames).To(HaveLen(1))
|
||||
Expect(payloadFrames[0].(*frames.StreamFrame).DataLenPresent).To(BeFalse())
|
||||
payloadFrames, err = packer.composeNextPacket(nil, publicHeaderLen)
|
||||
payloadFrames, err = packer.composeNextPacket(nil, maxFrameSize)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(payloadFrames).To(BeEmpty())
|
||||
})
|
||||
@@ -309,6 +321,18 @@ var _ = Describe("Packet packer", func() {
|
||||
Expect(p.frames[0].(*frames.StreamFrame).DataLenPresent).To(BeFalse())
|
||||
})
|
||||
|
||||
It("packs smaller packets when it is not yet forward-secure", func() {
|
||||
packer.isForwardSecure = false
|
||||
f := &frames.StreamFrame{
|
||||
StreamID: 3,
|
||||
Data: bytes.Repeat([]byte{'f'}, int(protocol.MaxPacketSize)),
|
||||
}
|
||||
streamFramer.AddFrameForRetransmission(f)
|
||||
p, err := packer.PackPacket(nil, nil, 0)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(p.raw).To(HaveLen(int(protocol.MaxPacketSize - protocol.NonForwardSecurePacketSizeReduction)))
|
||||
})
|
||||
|
||||
It("packs multiple small stream frames into single packet", func() {
|
||||
f1 := &frames.StreamFrame{
|
||||
StreamID: 5,
|
||||
@@ -350,17 +374,17 @@ var _ = Describe("Packet packer", func() {
|
||||
maxStreamFrameDataLen := protocol.MaxFrameAndPublicHeaderSize - publicHeaderLen - minLength
|
||||
f.Data = bytes.Repeat([]byte{'f'}, int(maxStreamFrameDataLen)+200)
|
||||
streamFramer.AddFrameForRetransmission(f)
|
||||
payloadFrames, err := packer.composeNextPacket(nil, publicHeaderLen)
|
||||
payloadFrames, err := packer.composeNextPacket(nil, maxFrameSize)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(payloadFrames).To(HaveLen(1))
|
||||
Expect(payloadFrames[0].(*frames.StreamFrame).DataLenPresent).To(BeFalse())
|
||||
Expect(payloadFrames[0].(*frames.StreamFrame).Data).To(HaveLen(int(maxStreamFrameDataLen)))
|
||||
payloadFrames, err = packer.composeNextPacket(nil, publicHeaderLen)
|
||||
payloadFrames, err = packer.composeNextPacket(nil, maxFrameSize)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(payloadFrames).To(HaveLen(1))
|
||||
Expect(payloadFrames[0].(*frames.StreamFrame).Data).To(HaveLen(200))
|
||||
Expect(payloadFrames[0].(*frames.StreamFrame).DataLenPresent).To(BeFalse())
|
||||
payloadFrames, err = packer.composeNextPacket(nil, publicHeaderLen)
|
||||
payloadFrames, err = packer.composeNextPacket(nil, maxFrameSize)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(payloadFrames).To(BeEmpty())
|
||||
})
|
||||
@@ -423,10 +447,10 @@ var _ = Describe("Packet packer", func() {
|
||||
f.Data = bytes.Repeat([]byte{'f'}, int(protocol.MaxFrameAndPublicHeaderSize-publicHeaderLen-minLength+2)) // + 2 since MinceLength is 1 bigger than the actual StreamFrame header
|
||||
|
||||
streamFramer.AddFrameForRetransmission(f)
|
||||
payloadFrames, err := packer.composeNextPacket(nil, publicHeaderLen)
|
||||
payloadFrames, err := packer.composeNextPacket(nil, maxFrameSize)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(payloadFrames).To(HaveLen(1))
|
||||
payloadFrames, err = packer.composeNextPacket(nil, publicHeaderLen)
|
||||
payloadFrames, err = packer.composeNextPacket(nil, maxFrameSize)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(payloadFrames).To(HaveLen(1))
|
||||
})
|
||||
@@ -491,7 +515,7 @@ var _ = Describe("Packet packer", func() {
|
||||
Data: bytes.Repeat([]byte{'f'}, length),
|
||||
}
|
||||
streamFramer.AddFrameForRetransmission(f)
|
||||
_, err := packer.composeNextPacket(nil, publicHeaderLen)
|
||||
_, err := packer.composeNextPacket(nil, maxFrameSize)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(packer.controlFrames[0]).To(Equal(&frames.BlockedFrame{StreamID: 5}))
|
||||
})
|
||||
@@ -504,7 +528,7 @@ var _ = Describe("Packet packer", func() {
|
||||
Data: bytes.Repeat([]byte{'f'}, length),
|
||||
}
|
||||
streamFramer.AddFrameForRetransmission(f)
|
||||
p, err := packer.composeNextPacket(nil, publicHeaderLen)
|
||||
p, err := packer.composeNextPacket(nil, maxFrameSize)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(p).To(HaveLen(1))
|
||||
Expect(p[0].(*frames.StreamFrame).DataLenPresent).To(BeFalse())
|
||||
@@ -517,7 +541,7 @@ var _ = Describe("Packet packer", func() {
|
||||
Data: []byte("foobar"),
|
||||
}
|
||||
streamFramer.AddFrameForRetransmission(f)
|
||||
_, err := packer.composeNextPacket(nil, publicHeaderLen)
|
||||
_, err := packer.composeNextPacket(nil, maxFrameSize)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(packer.controlFrames[0]).To(Equal(&frames.BlockedFrame{StreamID: 0}))
|
||||
})
|
||||
@@ -551,4 +575,111 @@ var _ = Describe("Packet packer", func() {
|
||||
Expect(p.frames).To(HaveLen(1))
|
||||
Expect(p.frames[0]).To(Equal(wuf))
|
||||
})
|
||||
|
||||
Context("retransmitting of handshake packets", func() {
|
||||
swf := &frames.StopWaitingFrame{LeastUnacked: 1}
|
||||
sf := &frames.StreamFrame{
|
||||
StreamID: 1,
|
||||
Data: []byte("foobar"),
|
||||
}
|
||||
|
||||
It("packs a retransmission for a packet sent with no encryption", func() {
|
||||
packet := &ackhandler.Packet{
|
||||
EncryptionLevel: protocol.EncryptionUnencrypted,
|
||||
Frames: []frames.Frame{sf},
|
||||
}
|
||||
p, err := packer.RetransmitNonForwardSecurePacket(swf, packet)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(p.frames).To(ContainElement(sf))
|
||||
Expect(p.frames).To(ContainElement(swf))
|
||||
Expect(p.encryptionLevel).To(Equal(protocol.EncryptionUnencrypted))
|
||||
// unencrypted packets don't need a diversification nonce
|
||||
Expect(packer.cryptoSetup.(*mockCryptoSetup).forcedDivNonce).To(BeFalse())
|
||||
})
|
||||
|
||||
It("packs a retransmission for a packet sent with initial encryption", func() {
|
||||
packet := &ackhandler.Packet{
|
||||
EncryptionLevel: protocol.EncryptionSecure,
|
||||
Frames: []frames.Frame{sf},
|
||||
}
|
||||
p, err := packer.RetransmitNonForwardSecurePacket(swf, packet)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(p.frames).To(ContainElement(sf))
|
||||
Expect(p.frames).To(ContainElement(swf))
|
||||
Expect(p.encryptionLevel).To(Equal(protocol.EncryptionSecure))
|
||||
})
|
||||
|
||||
It("includes the diversification nonce on packets sent with initial encryption", func() {
|
||||
packet := &ackhandler.Packet{
|
||||
EncryptionLevel: protocol.EncryptionSecure,
|
||||
Frames: []frames.Frame{sf},
|
||||
}
|
||||
p, err := packer.RetransmitNonForwardSecurePacket(swf, packet)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(p.encryptionLevel).To(Equal(protocol.EncryptionSecure))
|
||||
Expect(packer.cryptoSetup.(*mockCryptoSetup).forcedDivNonce).To(BeTrue())
|
||||
})
|
||||
|
||||
It("removes non-retransmittable frames", func() {
|
||||
wuf := &frames.WindowUpdateFrame{StreamID: 5, ByteOffset: 10}
|
||||
packet := &ackhandler.Packet{
|
||||
EncryptionLevel: protocol.EncryptionSecure,
|
||||
Frames: []frames.Frame{
|
||||
sf,
|
||||
&frames.StopWaitingFrame{},
|
||||
wuf,
|
||||
&frames.AckFrame{},
|
||||
},
|
||||
}
|
||||
p, err := packer.RetransmitNonForwardSecurePacket(swf, packet)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(p.frames).To(HaveLen(3))
|
||||
Expect(p.frames).To(ContainElement(sf))
|
||||
Expect(p.frames).To(ContainElement(swf))
|
||||
Expect(p.frames).To(ContainElement(wuf))
|
||||
Expect(p.encryptionLevel).To(Equal(protocol.EncryptionSecure))
|
||||
})
|
||||
|
||||
It("doesn't pack a packet for a non-retransmittable packet", func() {
|
||||
packet := &ackhandler.Packet{
|
||||
EncryptionLevel: protocol.EncryptionSecure,
|
||||
Frames: []frames.Frame{&frames.AckFrame{}, &frames.StopWaitingFrame{}},
|
||||
}
|
||||
p, err := packer.RetransmitNonForwardSecurePacket(swf, packet)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(p).To(BeNil())
|
||||
})
|
||||
|
||||
// this should never happen, since non forward-secure packets are limited to a size smaller than MaxPacketSize, such that it is always possible to retransmit them without splitting the StreamFrame
|
||||
// (note that the retransmitted packet needs to have enough space for the StopWaitingFrame)
|
||||
It("refuses to send a packet larger than MaxPacketSize", func() {
|
||||
packet := &ackhandler.Packet{
|
||||
EncryptionLevel: protocol.EncryptionSecure,
|
||||
Frames: []frames.Frame{
|
||||
&frames.StreamFrame{
|
||||
StreamID: 1,
|
||||
Data: bytes.Repeat([]byte{'f'}, int(protocol.MaxPacketSize-5)),
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := packer.RetransmitNonForwardSecurePacket(swf, packet)
|
||||
Expect(err).To(MatchError("PacketPacker BUG: packet too large"))
|
||||
})
|
||||
|
||||
It("refuses to retransmit packets that were sent with forward-secure encryption", func() {
|
||||
p := &ackhandler.Packet{
|
||||
EncryptionLevel: protocol.EncryptionForwardSecure,
|
||||
}
|
||||
_, err := packer.RetransmitNonForwardSecurePacket(nil, p)
|
||||
Expect(err).To(MatchError("PacketPacker BUG: forward-secure encrypted handshake packets don't need special treatment"))
|
||||
})
|
||||
|
||||
It("refuses to retransmit packets without a StopWaitingFrame", func() {
|
||||
p := &ackhandler.Packet{
|
||||
EncryptionLevel: protocol.EncryptionSecure,
|
||||
}
|
||||
_, err := packer.RetransmitNonForwardSecurePacket(nil, p)
|
||||
Expect(err).To(MatchError("PacketPacker BUG: Handshake retransmissions must contain a StopWaitingFrame"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,6 +2,10 @@ package protocol
|
||||
|
||||
import "time"
|
||||
|
||||
// NonForwardSecurePacketSizeReduction is the number of bytes a non forward-secure packet has to be smaller than a forward-secure packet
|
||||
// This makes sure that those packets can always be retransmitted without splitting the contained StreamFrames
|
||||
const NonForwardSecurePacketSizeReduction = 50
|
||||
|
||||
// DefaultMaxCongestionWindow is the default for the max congestion window
|
||||
const DefaultMaxCongestionWindow = 1000
|
||||
|
||||
|
||||
79
session.go
79
session.go
@@ -236,6 +236,9 @@ runLoop:
|
||||
// begins with the public header and we never copy it.
|
||||
putPacketBuffer(p.publicHeader.Raw)
|
||||
case l := <-s.aeadChanged:
|
||||
if l == protocol.EncryptionForwardSecure {
|
||||
s.packer.SetForwardSecure()
|
||||
}
|
||||
s.tryDecryptingQueuedPackets()
|
||||
s.cryptoChangeCallback(s, l == protocol.EncryptionForwardSecure)
|
||||
}
|
||||
@@ -579,21 +582,39 @@ func (s *session) sendPacket() error {
|
||||
}
|
||||
utils.Debugf("\tDequeueing retransmission for packet 0x%x", retransmitPacket.PacketNumber)
|
||||
|
||||
// resend the frames that were in the packet
|
||||
for _, frame := range retransmitPacket.GetFramesForRetransmission() {
|
||||
switch frame.(type) {
|
||||
case *frames.StreamFrame:
|
||||
s.streamFramer.AddFrameForRetransmission(frame.(*frames.StreamFrame))
|
||||
case *frames.WindowUpdateFrame:
|
||||
// only retransmit WindowUpdates if the stream is not yet closed and the we haven't sent another WindowUpdate with a higher ByteOffset for the stream
|
||||
var currentOffset protocol.ByteCount
|
||||
f := frame.(*frames.WindowUpdateFrame)
|
||||
currentOffset, err = s.flowControlManager.GetReceiveWindow(f.StreamID)
|
||||
if err == nil && f.ByteOffset >= currentOffset {
|
||||
if retransmitPacket.EncryptionLevel != protocol.EncryptionForwardSecure {
|
||||
utils.Debugf("\tDequeueing handshake retransmission for packet 0x%x", retransmitPacket.PacketNumber)
|
||||
stopWaitingFrame := s.sentPacketHandler.GetStopWaitingFrame(true)
|
||||
var packet *packedPacket
|
||||
packet, err = s.packer.RetransmitNonForwardSecurePacket(stopWaitingFrame, retransmitPacket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if packet == nil {
|
||||
continue
|
||||
}
|
||||
err = s.sendPackedPacket(packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
// resend the frames that were in the packet
|
||||
for _, frame := range retransmitPacket.GetFramesForRetransmission() {
|
||||
switch frame.(type) {
|
||||
case *frames.StreamFrame:
|
||||
s.streamFramer.AddFrameForRetransmission(frame.(*frames.StreamFrame))
|
||||
case *frames.WindowUpdateFrame:
|
||||
// only retransmit WindowUpdates if the stream is not yet closed and the we haven't sent another WindowUpdate with a higher ByteOffset for the stream
|
||||
var currentOffset protocol.ByteCount
|
||||
f := frame.(*frames.WindowUpdateFrame)
|
||||
currentOffset, err = s.flowControlManager.GetReceiveWindow(f.StreamID)
|
||||
if err == nil && f.ByteOffset >= currentOffset {
|
||||
controlFrames = append(controlFrames, frame)
|
||||
}
|
||||
default:
|
||||
controlFrames = append(controlFrames, frame)
|
||||
}
|
||||
default:
|
||||
controlFrames = append(controlFrames, frame)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -619,19 +640,7 @@ func (s *session) sendPacket() error {
|
||||
s.packer.QueueControlFrameForNextPacket(f)
|
||||
}
|
||||
|
||||
err = s.sentPacketHandler.SentPacket(&ackhandler.Packet{
|
||||
PacketNumber: packet.number,
|
||||
Frames: packet.frames,
|
||||
Length: protocol.ByteCount(len(packet.raw)),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.logPacket(packet)
|
||||
|
||||
err = s.conn.Write(packet.raw)
|
||||
putPacketBuffer(packet.raw)
|
||||
err = s.sendPackedPacket(packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -639,6 +648,24 @@ func (s *session) sendPacket() error {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *session) sendPackedPacket(packet *packedPacket) error {
|
||||
err := s.sentPacketHandler.SentPacket(&ackhandler.Packet{
|
||||
PacketNumber: packet.number,
|
||||
Frames: packet.frames,
|
||||
Length: protocol.ByteCount(len(packet.raw)),
|
||||
EncryptionLevel: packet.encryptionLevel,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.logPacket(packet)
|
||||
|
||||
err = s.conn.Write(packet.raw)
|
||||
putPacketBuffer(packet.raw)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *session) sendConnectionClose(quicErr *qerr.QuicError) error {
|
||||
packet, err := s.packer.PackConnectionClose(&frames.ConnectionCloseFrame{ErrorCode: quicErr.ErrorCode, ReasonPhrase: quicErr.ErrorMessage}, s.sentPacketHandler.GetLeastUnacked())
|
||||
if err != nil {
|
||||
|
||||
395
session_test.go
395
session_test.go
@@ -800,192 +800,261 @@ var _ = Describe("Session", func() {
|
||||
Expect(mconn.written).To(HaveLen(1))
|
||||
Expect(mconn.written[0]).To(ContainSubstring(string([]byte("PRST"))))
|
||||
})
|
||||
|
||||
It("informs the SentPacketHandler about sent packets", func() {
|
||||
sess.sentPacketHandler = newMockSentPacketHandler()
|
||||
sess.packer.packetNumberGenerator.next = 0x1337 + 9
|
||||
sess.packer.cryptoSetup = &mockCryptoSetup{encLevelSeal: protocol.EncryptionSecure}
|
||||
|
||||
f := &frames.StreamFrame{
|
||||
StreamID: 5,
|
||||
Data: []byte("foobar"),
|
||||
}
|
||||
sess.streamFramer.AddFrameForRetransmission(f)
|
||||
_, err := sess.GetOrOpenStream(5)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = sess.sendPacket()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(mconn.written).To(HaveLen(1))
|
||||
sentPackets := sess.sentPacketHandler.(*mockSentPacketHandler).sentPackets
|
||||
Expect(sentPackets).To(HaveLen(1))
|
||||
Expect(sentPackets[0].Frames).To(ContainElement(f))
|
||||
Expect(sentPackets[0].EncryptionLevel).To(Equal(protocol.EncryptionSecure))
|
||||
Expect(sentPackets[0].Length).To(BeEquivalentTo(len(mconn.written[0])))
|
||||
})
|
||||
})
|
||||
|
||||
Context("retransmissions", func() {
|
||||
var sph *mockSentPacketHandler
|
||||
BeforeEach(func() {
|
||||
// a StopWaitingFrame is added, so make sure the packet number of the new package is higher than the packet number of the retransmitted packet
|
||||
sess.packer.packetNumberGenerator.next = 0x1337 + 10
|
||||
sph = newMockSentPacketHandler().(*mockSentPacketHandler)
|
||||
sess.sentPacketHandler = sph
|
||||
sess.packer.cryptoSetup = &mockCryptoSetup{encLevelSeal: protocol.EncryptionForwardSecure}
|
||||
})
|
||||
|
||||
It("sends a StreamFrame from a packet queued for retransmission", func() {
|
||||
// a StopWaitingFrame is added, so make sure the packet number of the new package is higher than the packet number of the retransmitted packet
|
||||
sess.packer.packetNumberGenerator.next = 0x1337 + 9
|
||||
Context("for handshake packets", func() {
|
||||
It("retransmits an unencrypted packet", func() {
|
||||
sf := &frames.StreamFrame{StreamID: 1, Data: []byte("foobar")}
|
||||
sph.retransmissionQueue = []*ackhandler.Packet{{
|
||||
Frames: []frames.Frame{sf},
|
||||
EncryptionLevel: protocol.EncryptionUnencrypted,
|
||||
}}
|
||||
err := sess.sendPacket()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(mconn.written).To(HaveLen(1))
|
||||
sentPackets := sph.sentPackets
|
||||
Expect(sentPackets).To(HaveLen(1))
|
||||
Expect(sentPackets[0].EncryptionLevel).To(Equal(protocol.EncryptionUnencrypted))
|
||||
Expect(sentPackets[0].Frames).To(HaveLen(2))
|
||||
Expect(sentPackets[0].Frames[1]).To(Equal(sf))
|
||||
swf := sentPackets[0].Frames[0].(*frames.StopWaitingFrame)
|
||||
Expect(swf.LeastUnacked).To(Equal(protocol.PacketNumber(0x1337)))
|
||||
})
|
||||
|
||||
f := frames.StreamFrame{
|
||||
StreamID: 0x5,
|
||||
Data: []byte("foobar1234567"),
|
||||
}
|
||||
p := ackhandler.Packet{
|
||||
PacketNumber: 0x1337,
|
||||
Frames: []frames.Frame{&f},
|
||||
}
|
||||
sph := newMockSentPacketHandler()
|
||||
sph.(*mockSentPacketHandler).retransmissionQueue = []*ackhandler.Packet{&p}
|
||||
sess.sentPacketHandler = sph
|
||||
It("doesn't retransmit non-retransmittable packets", func() {
|
||||
sph.retransmissionQueue = []*ackhandler.Packet{{
|
||||
Frames: []frames.Frame{
|
||||
&frames.AckFrame{},
|
||||
&frames.StopWaitingFrame{},
|
||||
},
|
||||
EncryptionLevel: protocol.EncryptionUnencrypted,
|
||||
}}
|
||||
err := sess.sendPacket()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(mconn.written).To(BeEmpty())
|
||||
})
|
||||
|
||||
err := sess.sendPacket()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(mconn.written).To(HaveLen(1))
|
||||
Expect(sph.(*mockSentPacketHandler).requestedStopWaiting).To(BeTrue())
|
||||
Expect(mconn.written[0]).To(ContainSubstring("foobar1234567"))
|
||||
It("retransmit a packet encrypted with the initial encryption", func() {
|
||||
sf := &frames.StreamFrame{StreamID: 1, Data: []byte("foobar")}
|
||||
sph.retransmissionQueue = []*ackhandler.Packet{{
|
||||
Frames: []frames.Frame{sf},
|
||||
EncryptionLevel: protocol.EncryptionSecure,
|
||||
}}
|
||||
err := sess.sendPacket()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(mconn.written).To(HaveLen(1))
|
||||
sentPackets := sph.sentPackets
|
||||
Expect(sentPackets).To(HaveLen(1))
|
||||
Expect(sentPackets[0].EncryptionLevel).To(Equal(protocol.EncryptionSecure))
|
||||
Expect(sentPackets[0].Frames).To(HaveLen(2))
|
||||
Expect(sentPackets[0].Frames).To(ContainElement(sf))
|
||||
})
|
||||
})
|
||||
|
||||
It("sends a StreamFrame from a packet queued for retransmission", func() {
|
||||
// a StopWaitingFrame is added, so make sure the packet number of the new package is higher than the packet number of the retransmitted packet
|
||||
sess.packer.packetNumberGenerator.next = 0x1337 + 9
|
||||
Context("for packets after the handshake", func() {
|
||||
BeforeEach(func() {
|
||||
sess.packer.SetForwardSecure()
|
||||
})
|
||||
|
||||
f1 := frames.StreamFrame{
|
||||
StreamID: 0x5,
|
||||
Data: []byte("foobar"),
|
||||
}
|
||||
f2 := frames.StreamFrame{
|
||||
StreamID: 0x7,
|
||||
Data: []byte("loremipsum"),
|
||||
}
|
||||
p1 := ackhandler.Packet{
|
||||
PacketNumber: 0x1337,
|
||||
Frames: []frames.Frame{&f1},
|
||||
}
|
||||
p2 := ackhandler.Packet{
|
||||
PacketNumber: 0x1338,
|
||||
Frames: []frames.Frame{&f2},
|
||||
}
|
||||
sph := newMockSentPacketHandler()
|
||||
sph.(*mockSentPacketHandler).retransmissionQueue = []*ackhandler.Packet{&p1, &p2}
|
||||
sess.sentPacketHandler = sph
|
||||
It("sends a StreamFrame from a packet queued for retransmission", func() {
|
||||
f := frames.StreamFrame{
|
||||
StreamID: 0x5,
|
||||
Data: []byte("foobar1234567"),
|
||||
}
|
||||
p := ackhandler.Packet{
|
||||
PacketNumber: 0x1337,
|
||||
Frames: []frames.Frame{&f},
|
||||
EncryptionLevel: protocol.EncryptionForwardSecure,
|
||||
}
|
||||
sph.retransmissionQueue = []*ackhandler.Packet{&p}
|
||||
|
||||
err := sess.sendPacket()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(mconn.written).To(HaveLen(1))
|
||||
Expect(mconn.written[0]).To(ContainSubstring("foobar"))
|
||||
Expect(mconn.written[0]).To(ContainSubstring("loremipsum"))
|
||||
})
|
||||
err := sess.sendPacket()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(mconn.written).To(HaveLen(1))
|
||||
Expect(sph.requestedStopWaiting).To(BeTrue())
|
||||
Expect(mconn.written[0]).To(ContainSubstring("foobar1234567"))
|
||||
})
|
||||
|
||||
It("always attaches a StopWaiting to a packet that contains a retransmission", func() {
|
||||
// make sure the packet number of the new package is higher than the packet number of the retransmitted packet
|
||||
sess.packer.packetNumberGenerator.next = 0x1337 + 9
|
||||
It("sends a StreamFrame from a packet queued for retransmission", func() {
|
||||
f1 := frames.StreamFrame{
|
||||
StreamID: 0x5,
|
||||
Data: []byte("foobar"),
|
||||
}
|
||||
f2 := frames.StreamFrame{
|
||||
StreamID: 0x7,
|
||||
Data: []byte("loremipsum"),
|
||||
}
|
||||
p1 := ackhandler.Packet{
|
||||
PacketNumber: 0x1337,
|
||||
Frames: []frames.Frame{&f1},
|
||||
EncryptionLevel: protocol.EncryptionForwardSecure,
|
||||
}
|
||||
p2 := ackhandler.Packet{
|
||||
PacketNumber: 0x1338,
|
||||
Frames: []frames.Frame{&f2},
|
||||
EncryptionLevel: protocol.EncryptionForwardSecure,
|
||||
}
|
||||
sph.retransmissionQueue = []*ackhandler.Packet{&p1, &p2}
|
||||
|
||||
f := &frames.StreamFrame{
|
||||
StreamID: 0x5,
|
||||
Data: bytes.Repeat([]byte{'f'}, int(1.5*float32(protocol.MaxPacketSize))),
|
||||
}
|
||||
sess.streamFramer.AddFrameForRetransmission(f)
|
||||
err := sess.sendPacket()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(mconn.written).To(HaveLen(1))
|
||||
Expect(mconn.written[0]).To(ContainSubstring("foobar"))
|
||||
Expect(mconn.written[0]).To(ContainSubstring("loremipsum"))
|
||||
})
|
||||
|
||||
sph := newMockSentPacketHandler()
|
||||
sess.sentPacketHandler = sph
|
||||
It("always attaches a StopWaiting to a packet that contains a retransmission", func() {
|
||||
f := &frames.StreamFrame{
|
||||
StreamID: 0x5,
|
||||
Data: bytes.Repeat([]byte{'f'}, int(1.5*float32(protocol.MaxPacketSize))),
|
||||
}
|
||||
sess.streamFramer.AddFrameForRetransmission(f)
|
||||
|
||||
err := sess.sendPacket()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(mconn.written).To(HaveLen(2))
|
||||
sentPackets := sph.(*mockSentPacketHandler).sentPackets
|
||||
Expect(sentPackets).To(HaveLen(2))
|
||||
_, ok := sentPackets[0].Frames[0].(*frames.StopWaitingFrame)
|
||||
Expect(ok).To(BeTrue())
|
||||
_, ok = sentPackets[1].Frames[0].(*frames.StopWaitingFrame)
|
||||
Expect(ok).To(BeTrue())
|
||||
})
|
||||
err := sess.sendPacket()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(mconn.written).To(HaveLen(2))
|
||||
sentPackets := sph.sentPackets
|
||||
Expect(sentPackets).To(HaveLen(2))
|
||||
_, ok := sentPackets[0].Frames[0].(*frames.StopWaitingFrame)
|
||||
Expect(ok).To(BeTrue())
|
||||
_, ok = sentPackets[1].Frames[0].(*frames.StopWaitingFrame)
|
||||
Expect(ok).To(BeTrue())
|
||||
})
|
||||
|
||||
It("calls MaybeQueueRTOs even if congestion blocked, so that bytesInFlight is updated", func() {
|
||||
sph := newMockSentPacketHandler()
|
||||
sph.(*mockSentPacketHandler).congestionLimited = true
|
||||
sess.sentPacketHandler = sph
|
||||
err := sess.sendPacket()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(sph.(*mockSentPacketHandler).maybeQueueRTOsCalled).To(BeTrue())
|
||||
})
|
||||
It("calls MaybeQueueRTOs even if congestion blocked, so that bytesInFlight is updated", func() {
|
||||
sph.congestionLimited = true
|
||||
sess.sentPacketHandler = sph
|
||||
err := sess.sendPacket()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(sph.maybeQueueRTOsCalled).To(BeTrue())
|
||||
})
|
||||
|
||||
It("retransmits a WindowUpdates if it hasn't already sent a WindowUpdate with a higher ByteOffset", func() {
|
||||
_, err := sess.GetOrOpenStream(5)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
fc := newMockFlowControlHandler()
|
||||
fc.receiveWindow = 0x1000
|
||||
sess.flowControlManager = fc
|
||||
sph := newMockSentPacketHandler()
|
||||
sess.sentPacketHandler = sph
|
||||
wuf := &frames.WindowUpdateFrame{
|
||||
StreamID: 5,
|
||||
ByteOffset: 0x1000,
|
||||
}
|
||||
sph.(*mockSentPacketHandler).retransmissionQueue = []*ackhandler.Packet{{
|
||||
Frames: []frames.Frame{wuf},
|
||||
}}
|
||||
err = sess.sendPacket()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
sentPackets := sph.(*mockSentPacketHandler).sentPackets
|
||||
Expect(sentPackets).To(HaveLen(1))
|
||||
Expect(sentPackets[0].Frames).To(ContainElement(wuf))
|
||||
})
|
||||
|
||||
It("doesn't retransmit WindowUpdates if it already sent a WindowUpdate with a higher ByteOffset", func() {
|
||||
_, err := sess.GetOrOpenStream(5)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
fc := newMockFlowControlHandler()
|
||||
fc.receiveWindow = 0x2000
|
||||
sess.flowControlManager = fc
|
||||
sph := newMockSentPacketHandler()
|
||||
sess.sentPacketHandler = sph
|
||||
sph.(*mockSentPacketHandler).retransmissionQueue = []*ackhandler.Packet{{
|
||||
Frames: []frames.Frame{&frames.WindowUpdateFrame{
|
||||
It("retransmits a WindowUpdates if it hasn't already sent a WindowUpdate with a higher ByteOffset", func() {
|
||||
_, err := sess.GetOrOpenStream(5)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
fc := newMockFlowControlHandler()
|
||||
fc.receiveWindow = 0x1000
|
||||
sess.flowControlManager = fc
|
||||
wuf := &frames.WindowUpdateFrame{
|
||||
StreamID: 5,
|
||||
ByteOffset: 0x1000,
|
||||
}},
|
||||
}}
|
||||
err = sess.sendPacket()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(sph.(*mockSentPacketHandler).sentPackets).To(BeEmpty())
|
||||
})
|
||||
}
|
||||
sph.retransmissionQueue = []*ackhandler.Packet{{
|
||||
Frames: []frames.Frame{wuf},
|
||||
EncryptionLevel: protocol.EncryptionForwardSecure,
|
||||
}}
|
||||
err = sess.sendPacket()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(sph.sentPackets).To(HaveLen(1))
|
||||
Expect(sph.sentPackets[0].Frames).To(ContainElement(wuf))
|
||||
})
|
||||
|
||||
It("doesn't retransmit WindowUpdates for closed streams", func() {
|
||||
str, err := sess.GetOrOpenStream(5)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// close the stream
|
||||
str.(*stream).sentFin()
|
||||
str.Close()
|
||||
str.(*stream).RegisterRemoteError(nil)
|
||||
sess.garbageCollectStreams()
|
||||
_, err = sess.flowControlManager.SendWindowSize(5)
|
||||
Expect(err).To(MatchError("Error accessing the flowController map."))
|
||||
sph := newMockSentPacketHandler()
|
||||
sess.sentPacketHandler = sph
|
||||
sph.(*mockSentPacketHandler).retransmissionQueue = []*ackhandler.Packet{{
|
||||
Frames: []frames.Frame{&frames.WindowUpdateFrame{
|
||||
StreamID: 5,
|
||||
ByteOffset: 0x1337,
|
||||
}},
|
||||
}}
|
||||
err = sess.sendPacket()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
sentPackets := sph.(*mockSentPacketHandler).sentPackets
|
||||
Expect(sentPackets).To(BeEmpty())
|
||||
})
|
||||
It("doesn't retransmit WindowUpdates if it already sent a WindowUpdate with a higher ByteOffset", func() {
|
||||
_, err := sess.GetOrOpenStream(5)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
fc := newMockFlowControlHandler()
|
||||
fc.receiveWindow = 0x2000
|
||||
sess.flowControlManager = fc
|
||||
sph.retransmissionQueue = []*ackhandler.Packet{{
|
||||
Frames: []frames.Frame{&frames.WindowUpdateFrame{
|
||||
StreamID: 5,
|
||||
ByteOffset: 0x1000,
|
||||
}},
|
||||
EncryptionLevel: protocol.EncryptionForwardSecure,
|
||||
}}
|
||||
err = sess.sendPacket()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(sph.sentPackets).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("retransmits RTO packets", func() {
|
||||
// We simulate consistently low RTTs, so that the test works faster
|
||||
n := protocol.PacketNumber(10)
|
||||
for p := protocol.PacketNumber(1); p < n; p++ {
|
||||
err := sess.sentPacketHandler.SentPacket(&ackhandler.Packet{PacketNumber: p, Length: 1})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
time.Sleep(time.Microsecond)
|
||||
ack := &frames.AckFrame{}
|
||||
ack.LargestAcked = p
|
||||
err = sess.sentPacketHandler.ReceivedAck(ack, p, time.Now())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
sess.packer.packetNumberGenerator.next = n + 1
|
||||
// Now, we send a single packet, and expect that it was retransmitted later
|
||||
It("doesn't retransmit WindowUpdates for closed streams", func() {
|
||||
str, err := sess.GetOrOpenStream(5)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// close the stream
|
||||
str.(*stream).sentFin()
|
||||
str.Close()
|
||||
str.(*stream).RegisterRemoteError(nil)
|
||||
sess.garbageCollectStreams()
|
||||
_, err = sess.flowControlManager.SendWindowSize(5)
|
||||
Expect(err).To(MatchError("Error accessing the flowController map."))
|
||||
sph.retransmissionQueue = []*ackhandler.Packet{{
|
||||
Frames: []frames.Frame{&frames.WindowUpdateFrame{
|
||||
StreamID: 5,
|
||||
ByteOffset: 0x1337,
|
||||
}},
|
||||
EncryptionLevel: protocol.EncryptionForwardSecure,
|
||||
}}
|
||||
err = sess.sendPacket()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(sph.sentPackets).To(BeEmpty())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
It("retransmits RTO packets", func() {
|
||||
sess.packer.cryptoSetup = &mockCryptoSetup{encLevelSeal: protocol.EncryptionForwardSecure}
|
||||
// We simulate consistently low RTTs, so that the test works faster
|
||||
n := protocol.PacketNumber(10)
|
||||
for p := protocol.PacketNumber(1); p < n; p++ {
|
||||
err := sess.sentPacketHandler.SentPacket(&ackhandler.Packet{
|
||||
PacketNumber: n,
|
||||
Length: 1,
|
||||
Frames: []frames.Frame{&frames.StreamFrame{
|
||||
Data: []byte("foobar"),
|
||||
}},
|
||||
PacketNumber: p,
|
||||
Length: 1,
|
||||
EncryptionLevel: protocol.EncryptionForwardSecure,
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
go sess.run()
|
||||
sess.scheduleSending()
|
||||
Eventually(func() [][]byte { return mconn.written }).ShouldNot(BeEmpty())
|
||||
Expect(mconn.written[0]).To(ContainSubstring("foobar"))
|
||||
time.Sleep(time.Microsecond)
|
||||
ack := &frames.AckFrame{}
|
||||
ack.LargestAcked = p
|
||||
err = sess.sentPacketHandler.ReceivedAck(ack, p, time.Now())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
sess.packer.packetNumberGenerator.next = n + 1
|
||||
// Now, we send a single packet, and expect that it was retransmitted later
|
||||
err := sess.sentPacketHandler.SentPacket(&ackhandler.Packet{
|
||||
PacketNumber: n,
|
||||
Length: 1,
|
||||
Frames: []frames.Frame{&frames.StreamFrame{
|
||||
Data: []byte("foobar"),
|
||||
}},
|
||||
EncryptionLevel: protocol.EncryptionForwardSecure,
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
go sess.run()
|
||||
sess.scheduleSending()
|
||||
Eventually(func() [][]byte { return mconn.written }).ShouldNot(BeEmpty())
|
||||
Expect(mconn.written[0]).To(ContainSubstring("foobar"))
|
||||
})
|
||||
|
||||
Context("scheduling sending", func() {
|
||||
@@ -1083,6 +1152,14 @@ var _ = Describe("Session", func() {
|
||||
})
|
||||
})
|
||||
|
||||
It("tells the packetPacker when forward-secure encryption is used", func() {
|
||||
go sess.run()
|
||||
sess.aeadChanged <- protocol.EncryptionSecure
|
||||
Consistently(func() bool { return sess.packer.isForwardSecure }).Should(BeFalse())
|
||||
sess.aeadChanged <- protocol.EncryptionForwardSecure
|
||||
Eventually(func() bool { return sess.packer.isForwardSecure }).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("closes when crypto stream errors", func() {
|
||||
go sess.run()
|
||||
s, err := sess.GetOrOpenStream(3)
|
||||
|
||||
Reference in New Issue
Block a user