From 599926e3cba157a31f499fbb3d9f29053c5b96fc Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sat, 25 Feb 2017 14:18:55 +0700 Subject: [PATCH 1/8] add a method to force the encryption level when sealing a packet --- handshake/crypto_setup_client.go | 19 ++++++++++++ handshake/crypto_setup_client_test.go | 42 +++++++++++++++++++++++++++ handshake/crypto_setup_interface.go | 1 + handshake/crypto_setup_server.go | 20 +++++++++++++ handshake/crypto_setup_server_test.go | 42 +++++++++++++++++++++++++++ packet_packer_test.go | 3 ++ 6 files changed, 127 insertions(+) diff --git a/handshake/crypto_setup_client.go b/handshake/crypto_setup_client.go index cb43b49d..50ca9227 100644 --- a/handshake/crypto_setup_client.go +++ b/handshake/crypto_setup_client.go @@ -312,6 +312,25 @@ func (h *cryptoSetupClient) Seal(dst, src []byte, packetNumber protocol.PacketNu return (&crypto.NullAEAD{}).Seal(dst, src, packetNumber, associatedData), protocol.EncryptionUnencrypted } +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() []byte { panic("not needed for cryptoSetupClient") } diff --git a/handshake/crypto_setup_client_test.go b/handshake/crypto_setup_client_test.go index bece3b40..b8a88519 100644 --- a/handshake/crypto_setup_client_test.go +++ b/handshake/crypto_setup_client_test.go @@ -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() { diff --git a/handshake/crypto_setup_interface.go b/handshake/crypto_setup_interface.go index 42cd923c..49952525 100644 --- a/handshake/crypto_setup_interface.go +++ b/handshake/crypto_setup_interface.go @@ -7,6 +7,7 @@ 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 diff --git a/handshake/crypto_setup_server.go b/handshake/crypto_setup_server.go index 7176b861..fe6fcef2 100644 --- a/handshake/crypto_setup_server.go +++ b/handshake/crypto_setup_server.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/rand" "encoding/binary" + "errors" "io" "sync" @@ -195,6 +196,25 @@ func (h *cryptoSetupServer) Seal(dst, src []byte, packetNumber protocol.PacketNu } } +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 diff --git a/handshake/crypto_setup_server_test.go b/handshake/crypto_setup_server_test.go index f90a8467..39313ff0 100644 --- a/handshake/crypto_setup_server_test.go +++ b/handshake/crypto_setup_server_test.go @@ -659,6 +659,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() { + 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")) + }) + }) }) Context("STK verification and creation", func() { diff --git a/packet_packer_test.go b/packet_packer_test.go index b76918a5..6e7f5025 100644 --- a/packet_packer_test.go +++ b/packet_packer_test.go @@ -24,6 +24,9 @@ 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) SealWith(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte, encLevel protocol.EncryptionLevel) ([]byte, protocol.EncryptionLevel, error) { + panic("not implemented") +} func (m *mockCryptoSetup) LockForSealing() {} func (m *mockCryptoSetup) UnlockForSealing() {} func (m *mockCryptoSetup) HandshakeComplete() bool { return m.handshakeComplete } From d4b2b6627385e872be591b22588023b2c9b78254 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Wed, 11 Jan 2017 12:01:41 +0700 Subject: [PATCH 2/8] change composeNextPacket function signature in packetPacker --- packet_packer.go | 7 +++---- packet_packer_test.go | 32 +++++++++++++++++--------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/packet_packer.go b/packet_packer.go index fafb70be..e1833ea4 100644 --- a/packet_packer.go +++ b/packet_packer.go @@ -104,7 +104,8 @@ func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, lea if isConnectionClose { payloadFrames = []frames.Frame{p.controlFrames[0]} } else { - payloadFrames, err = p.composeNextPacket(stopWaitingFrame, publicHeaderLength) + maxSize := protocol.MaxFrameAndPublicHeaderSize - publicHeaderLength + payloadFrames, err = p.composeNextPacket(stopWaitingFrame, maxSize) if err != nil { return nil, err } @@ -164,12 +165,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) diff --git a/packet_packer_test.go b/packet_packer_test.go index 6e7f5025..24e9f389 100644 --- a/packet_packer_test.go +++ b/packet_packer_test.go @@ -37,6 +37,7 @@ var _ = Describe("Packet packer", func() { var ( packer *packetPacker publicHeaderLen protocol.ByteCount + maxFrameSize protocol.ByteCount streamFramer *streamFramer ) @@ -57,6 +58,7 @@ var _ = Describe("Packet packer", func() { perspective: protocol.PerspectiveServer, } publicHeaderLen = 1 + 8 + 2 // 1 flag byte, 8 connection ID, 2 packet number + maxFrameSize = protocol.MaxFrameAndPublicHeaderSize - publicHeaderLen packer.version = protocol.Version34 }) @@ -222,10 +224,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()) }) @@ -241,10 +243,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)) }) @@ -275,14 +277,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()) }) @@ -353,17 +355,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()) }) @@ -426,10 +428,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)) }) @@ -494,7 +496,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})) }) @@ -507,7 +509,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()) @@ -520,7 +522,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})) }) From 9e147714ac65c950301215ea422fa8f83cbb1e1e Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sat, 25 Feb 2017 14:57:59 +0700 Subject: [PATCH 3/8] pack smaller packets as long as the encryption is not forward secure --- handshake/crypto_setup_server.go | 2 ++ handshake/crypto_setup_server_test.go | 6 +++++- packet_packer.go | 9 +++++++++ packet_packer_test.go | 13 +++++++++++++ protocol/server_parameters.go | 4 ++++ session.go | 3 +++ session_test.go | 8 ++++++++ 7 files changed, 44 insertions(+), 1 deletion(-) diff --git a/handshake/crypto_setup_server.go b/handshake/crypto_setup_server.go index fe6fcef2..33bf49c3 100644 --- a/handshake/crypto_setup_server.go +++ b/handshake/crypto_setup_server.go @@ -334,6 +334,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) diff --git a/handshake/crypto_setup_server_test.go b/handshake/crypto_setup_server_test.go index 39313ff0..6232a151 100644 --- a/handshake/crypto_setup_server_test.go +++ b/handshake/crypto_setup_server_test.go @@ -336,7 +336,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() { diff --git a/packet_packer.go b/packet_packer.go index e1833ea4..6bf5515f 100644 --- a/packet_packer.go +++ b/packet_packer.go @@ -23,6 +23,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 @@ -105,6 +107,9 @@ func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, lea payloadFrames = []frames.Frame{p.controlFrames[0]} } else { maxSize := protocol.MaxFrameAndPublicHeaderSize - publicHeaderLength + if !p.isForwardSecure { + maxSize -= protocol.NonForwardSecurePacketSizeReduction + } payloadFrames, err = p.composeNextPacket(stopWaitingFrame, maxSize) if err != nil { return nil, err @@ -218,3 +223,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 +} diff --git a/packet_packer_test.go b/packet_packer_test.go index 24e9f389..c6c46d87 100644 --- a/packet_packer_test.go +++ b/packet_packer_test.go @@ -60,6 +60,7 @@ var _ = Describe("Packet packer", func() { 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() { @@ -314,6 +315,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, diff --git a/protocol/server_parameters.go b/protocol/server_parameters.go index d7173045..1b2c9b40 100644 --- a/protocol/server_parameters.go +++ b/protocol/server_parameters.go @@ -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 diff --git a/session.go b/session.go index 53271e78..c9d31239 100644 --- a/session.go +++ b/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) } diff --git a/session_test.go b/session_test.go index 519cc300..07b8eecb 100644 --- a/session_test.go +++ b/session_test.go @@ -1083,6 +1083,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) From 7d2922d7ab7d7a35c2a09014b1c6ec689928af50 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sat, 25 Feb 2017 15:30:25 +0700 Subject: [PATCH 4/8] save the encryption level of a sent packet in the SentPacketHandler --- ackhandler/packet.go | 7 ++++--- session.go | 7 ++++--- session_test.go | 22 ++++++++++++++++++++++ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/ackhandler/packet.go b/ackhandler/packet.go index d748547e..17218854 100644 --- a/ackhandler/packet.go +++ b/ackhandler/packet.go @@ -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 diff --git a/session.go b/session.go index c9d31239..c3f9efd6 100644 --- a/session.go +++ b/session.go @@ -623,9 +623,10 @@ func (s *session) sendPacket() error { } err = s.sentPacketHandler.SentPacket(&ackhandler.Packet{ - PacketNumber: packet.number, - Frames: packet.frames, - Length: protocol.ByteCount(len(packet.raw)), + PacketNumber: packet.number, + Frames: packet.frames, + Length: protocol.ByteCount(len(packet.raw)), + EncryptionLevel: packet.encryptionLevel, }) if err != nil { return err diff --git a/session_test.go b/session_test.go index 07b8eecb..0075a3d4 100644 --- a/session_test.go +++ b/session_test.go @@ -800,6 +800,28 @@ 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() { From 5e91f139cee4f2b290976a8ed8a35fc08823bab4 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sat, 25 Feb 2017 15:47:54 +0700 Subject: [PATCH 5/8] add a method to pack a retransmission in the packetPacker --- packet_packer.go | 48 ++++++++++++++++++--- packet_packer_test.go | 97 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 138 insertions(+), 7 deletions(-) diff --git a/packet_packer.go b/packet_packer.go index 6bf5515f..df8ce311 100644 --- a/packet_packer.go +++ b/packet_packer.go @@ -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" @@ -51,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 @@ -59,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() @@ -103,7 +118,19 @@ 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 { maxSize := protocol.MaxFrameAndPublicHeaderSize - publicHeaderLength @@ -139,7 +166,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 } @@ -150,7 +177,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 { diff --git a/packet_packer_test.go b/packet_packer_test.go index c6c46d87..a1421080 100644 --- a/packet_packer_test.go +++ b/packet_packer_test.go @@ -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" @@ -25,7 +26,7 @@ func (m *mockCryptoSetup) Seal(dst, src []byte, packetNumber protocol.PacketNumb return append(src, bytes.Repeat([]byte{0}, 12)...), m.encLevelSeal } func (m *mockCryptoSetup) SealWith(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte, encLevel protocol.EncryptionLevel) ([]byte, protocol.EncryptionLevel, error) { - panic("not implemented") + return append(src, bytes.Repeat([]byte{0}, 12)...), encLevel, nil } func (m *mockCryptoSetup) LockForSealing() {} func (m *mockCryptoSetup) UnlockForSealing() {} @@ -569,4 +570,98 @@ 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)) + }) + + 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("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")) + }) + }) }) From 936a29ff357adb4caf70a2f574b42d0ea3c4c785 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sat, 25 Feb 2017 16:40:53 +0700 Subject: [PATCH 6/8] retransmit packets with the encryption level they were sent with --- session.go | 77 ++++++---- session_test.go | 365 +++++++++++++++++++++++++++--------------------- 2 files changed, 256 insertions(+), 186 deletions(-) diff --git a/session.go b/session.go index c3f9efd6..c83a87f4 100644 --- a/session.go +++ b/session.go @@ -582,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) } } } @@ -622,20 +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)), - EncryptionLevel: packet.encryptionLevel, - }) - 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 } @@ -643,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 { diff --git a/session_test.go b/session_test.go index 0075a3d4..674714c6 100644 --- a/session_test.go +++ b/session_test.go @@ -825,189 +825,236 @@ var _ = Describe("Session", func() { }) 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() { From b5c8c11c0c7350908eb3d6acac3fdcff0573daf5 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Wed, 11 Jan 2017 17:44:32 +0700 Subject: [PATCH 7/8] switch to forward-secure encryption after sending the SHLO --- benchmark_test.go | 1 + handshake/crypto_setup_server.go | 9 ++++++-- handshake/crypto_setup_server_test.go | 31 ++++++++++++++------------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/benchmark_test.go b/benchmark_test.go index 6b7bfd51..9319dee1 100644 --- a/benchmark_test.go +++ b/benchmark_test.go @@ -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 } diff --git a/handshake/crypto_setup_server.go b/handshake/crypto_setup_server.go index 33bf49c3..8bbd9262 100644 --- a/handshake/crypto_setup_server.go +++ b/handshake/crypto_setup_server.go @@ -31,6 +31,7 @@ type cryptoSetupServer struct { secureAEAD crypto.AEAD forwardSecureAEAD crypto.AEAD receivedForwardSecurePacket bool + sentSHLO bool receivedSecurePacket bool aeadChanged chan protocol.EncryptionLevel @@ -187,9 +188,12 @@ 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 @@ -375,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)) @@ -386,7 +391,7 @@ 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 { + if h.secureAEAD == nil || h.sentSHLO { return nil } return h.diversificationNonce diff --git a/handshake/crypto_setup_server_test.go b/handshake/crypto_setup_server_test.go index 6232a151..89b22ee9 100644 --- a/handshake/crypto_setup_server_test.go +++ b/handshake/crypto_setup_server_test.go @@ -194,8 +194,8 @@ var _ = Describe("Crypto setup", func() { Expect(cs.DiversificationNonce()).To(HaveLen(32)) }) - It("does not return nonce for FS packets", func() { - cs.receivedForwardSecurePacket = true + It("does not return nonce after sending the SHLO", func() { + cs.sentSHLO = true Expect(cs.DiversificationNonce()).To(BeEmpty()) }) @@ -633,15 +633,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{}) @@ -653,15 +644,25 @@ 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() { From 8c5e7818a03baf24c7f7be2befc8f73872bc53ac Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Wed, 1 Mar 2017 15:06:10 +0700 Subject: [PATCH 8/8] retransmit the diversification nonce in the packet carrying the SHLO The packet containing the SHLO is the only packet that is sent with initial encryption. If it is lost, we need to make sure that the diversification nonce is included in the PublicHeader, otherwise the client will not be able to derive the keys for the forward-secure encryption. --- handshake/crypto_setup_client.go | 2 +- handshake/crypto_setup_interface.go | 4 ++-- handshake/crypto_setup_server.go | 8 +++---- handshake/crypto_setup_server_test.go | 13 ++++++---- packet_packer.go | 3 ++- packet_packer_test.go | 34 ++++++++++++++++++++------- 6 files changed, 44 insertions(+), 20 deletions(-) diff --git a/handshake/crypto_setup_client.go b/handshake/crypto_setup_client.go index 50ca9227..2148ae53 100644 --- a/handshake/crypto_setup_client.go +++ b/handshake/crypto_setup_client.go @@ -331,7 +331,7 @@ func (h *cryptoSetupClient) SealWith(dst, src []byte, packetNumber protocol.Pack return nil, protocol.EncryptionUnspecified, errors.New("no encryption level specified") } -func (h *cryptoSetupClient) DiversificationNonce() []byte { +func (h *cryptoSetupClient) DiversificationNonce(bool) []byte { panic("not needed for cryptoSetupClient") } diff --git a/handshake/crypto_setup_interface.go b/handshake/crypto_setup_interface.go index 49952525..90e09bd4 100644 --- a/handshake/crypto_setup_interface.go +++ b/handshake/crypto_setup_interface.go @@ -12,6 +12,6 @@ type CryptoSetup interface { 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 } diff --git a/handshake/crypto_setup_server.go b/handshake/crypto_setup_server.go index 8bbd9262..4d3bf8e0 100644 --- a/handshake/crypto_setup_server.go +++ b/handshake/crypto_setup_server.go @@ -390,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.secureAEAD == nil || h.sentSHLO { - 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 { diff --git a/handshake/crypto_setup_server_test.go b/handshake/crypto_setup_server_test.go index 89b22ee9..a81e29f8 100644 --- a/handshake/crypto_setup_server_test.go +++ b/handshake/crypto_setup_server_test.go @@ -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 after sending the SHLO", func() { cs.sentSHLO = true - Expect(cs.DiversificationNonce()).To(BeEmpty()) + 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()) }) }) diff --git a/packet_packer.go b/packet_packer.go index df8ce311..10161899 100644 --- a/packet_packer.go +++ b/packet_packer.go @@ -93,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() { diff --git a/packet_packer_test.go b/packet_packer_test.go index a1421080..453c3f76 100644 --- a/packet_packer_test.go +++ b/packet_packer_test.go @@ -12,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 } @@ -28,10 +29,13 @@ func (m *mockCryptoSetup) Seal(dst, src []byte, packetNumber protocol.PacketNumb 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() []byte { return m.diversificationNonce } +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() { @@ -54,6 +58,7 @@ 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, @@ -99,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}, @@ -588,6 +593,8 @@ var _ = Describe("Packet packer", func() { 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() { @@ -602,6 +609,17 @@ var _ = Describe("Packet packer", func() { 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{