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:
Marten Seemann
2017-03-01 15:31:34 +07:00
committed by GitHub
12 changed files with 677 additions and 250 deletions

View File

@@ -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

View File

@@ -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
}

View File

@@ -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")
}

View File

@@ -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() {

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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"))
})
})
})

View File

@@ -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
}

View File

@@ -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"))
})
})
})

View File

@@ -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

View File

@@ -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 {

View File

@@ -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)