1package quic
2
3import (
4	"crypto/rand"
5	"fmt"
6
7	"github.com/lucas-clemente/quic-go/internal/protocol"
8	"github.com/lucas-clemente/quic-go/internal/qerr"
9	"github.com/lucas-clemente/quic-go/internal/wire"
10
11	. "github.com/onsi/ginkgo"
12	. "github.com/onsi/gomega"
13)
14
15func createHandshakeMessage(len int) []byte {
16	msg := make([]byte, 4+len)
17	rand.Read(msg[:1]) // random message type
18	msg[1] = uint8(len >> 16)
19	msg[2] = uint8(len >> 8)
20	msg[3] = uint8(len)
21	rand.Read(msg[4:])
22	return msg
23}
24
25var _ = Describe("Crypto Stream", func() {
26	var str cryptoStream
27
28	BeforeEach(func() {
29		str = newCryptoStream()
30	})
31
32	Context("handling incoming data", func() {
33		It("handles in-order CRYPTO frames", func() {
34			msg := createHandshakeMessage(6)
35			err := str.HandleCryptoFrame(&wire.CryptoFrame{Data: msg})
36			Expect(err).ToNot(HaveOccurred())
37			Expect(str.GetCryptoData()).To(Equal(msg))
38			Expect(str.GetCryptoData()).To(BeNil())
39		})
40
41		It("handles multiple messages in one CRYPTO frame", func() {
42			msg1 := createHandshakeMessage(6)
43			msg2 := createHandshakeMessage(10)
44			msg := append(append([]byte{}, msg1...), msg2...)
45			err := str.HandleCryptoFrame(&wire.CryptoFrame{Data: msg})
46			Expect(err).ToNot(HaveOccurred())
47			Expect(str.GetCryptoData()).To(Equal(msg1))
48			Expect(str.GetCryptoData()).To(Equal(msg2))
49			Expect(str.GetCryptoData()).To(BeNil())
50		})
51
52		It("errors if the frame exceeds the maximum offset", func() {
53			Expect(str.HandleCryptoFrame(&wire.CryptoFrame{
54				Offset: protocol.MaxCryptoStreamOffset - 5,
55				Data:   []byte("foobar"),
56			})).To(MatchError(&qerr.TransportError{
57				ErrorCode:    qerr.CryptoBufferExceeded,
58				ErrorMessage: fmt.Sprintf("received invalid offset %d on crypto stream, maximum allowed %d", protocol.MaxCryptoStreamOffset+1, protocol.MaxCryptoStreamOffset),
59			}))
60		})
61
62		It("handles messages split over multiple CRYPTO frames", func() {
63			msg := createHandshakeMessage(6)
64			err := str.HandleCryptoFrame(&wire.CryptoFrame{
65				Data: msg[:4],
66			})
67			Expect(err).ToNot(HaveOccurred())
68			Expect(str.GetCryptoData()).To(BeNil())
69			err = str.HandleCryptoFrame(&wire.CryptoFrame{
70				Offset: 4,
71				Data:   msg[4:],
72			})
73			Expect(err).ToNot(HaveOccurred())
74			Expect(str.GetCryptoData()).To(Equal(msg))
75			Expect(str.GetCryptoData()).To(BeNil())
76		})
77
78		It("handles out-of-order CRYPTO frames", func() {
79			msg := createHandshakeMessage(6)
80			err := str.HandleCryptoFrame(&wire.CryptoFrame{
81				Offset: 4,
82				Data:   msg[4:],
83			})
84			Expect(err).ToNot(HaveOccurred())
85			Expect(str.GetCryptoData()).To(BeNil())
86			err = str.HandleCryptoFrame(&wire.CryptoFrame{
87				Data: msg[:4],
88			})
89			Expect(err).ToNot(HaveOccurred())
90			Expect(str.GetCryptoData()).To(Equal(msg))
91			Expect(str.GetCryptoData()).To(BeNil())
92		})
93
94		Context("finishing", func() {
95			It("errors if there's still data to read after finishing", func() {
96				Expect(str.HandleCryptoFrame(&wire.CryptoFrame{
97					Data:   createHandshakeMessage(5),
98					Offset: 10,
99				})).To(Succeed())
100				Expect(str.Finish()).To(MatchError(&qerr.TransportError{
101					ErrorCode:    qerr.ProtocolViolation,
102					ErrorMessage: "encryption level changed, but crypto stream has more data to read",
103				}))
104			})
105
106			It("works with reordered data", func() {
107				f1 := &wire.CryptoFrame{
108					Data: []byte("foo"),
109				}
110				f2 := &wire.CryptoFrame{
111					Offset: 3,
112					Data:   []byte("bar"),
113				}
114				Expect(str.HandleCryptoFrame(f2)).To(Succeed())
115				Expect(str.HandleCryptoFrame(f1)).To(Succeed())
116				Expect(str.Finish()).To(Succeed())
117				Expect(str.HandleCryptoFrame(f2)).To(Succeed())
118			})
119
120			It("rejects new crypto data after finishing", func() {
121				Expect(str.Finish()).To(Succeed())
122				Expect(str.HandleCryptoFrame(&wire.CryptoFrame{
123					Data: createHandshakeMessage(5),
124				})).To(MatchError(&qerr.TransportError{
125					ErrorCode:    qerr.ProtocolViolation,
126					ErrorMessage: "received crypto data after change of encryption level",
127				}))
128			})
129
130			It("ignores crypto data below the maximum offset received before finishing", func() {
131				msg := createHandshakeMessage(15)
132				Expect(str.HandleCryptoFrame(&wire.CryptoFrame{
133					Data: msg,
134				})).To(Succeed())
135				Expect(str.GetCryptoData()).To(Equal(msg))
136				Expect(str.Finish()).To(Succeed())
137				Expect(str.HandleCryptoFrame(&wire.CryptoFrame{
138					Offset: protocol.ByteCount(len(msg) - 6),
139					Data:   []byte("foobar"),
140				})).To(Succeed())
141			})
142		})
143	})
144
145	Context("writing data", func() {
146		It("says if it has data", func() {
147			Expect(str.HasData()).To(BeFalse())
148			_, err := str.Write([]byte("foobar"))
149			Expect(err).ToNot(HaveOccurred())
150			Expect(str.HasData()).To(BeTrue())
151		})
152
153		It("pops crypto frames", func() {
154			_, err := str.Write([]byte("foobar"))
155			Expect(err).ToNot(HaveOccurred())
156			f := str.PopCryptoFrame(1000)
157			Expect(f).ToNot(BeNil())
158			Expect(f.Offset).To(BeZero())
159			Expect(f.Data).To(Equal([]byte("foobar")))
160		})
161
162		It("coalesces multiple writes", func() {
163			_, err := str.Write([]byte("foo"))
164			Expect(err).ToNot(HaveOccurred())
165			_, err = str.Write([]byte("bar"))
166			Expect(err).ToNot(HaveOccurred())
167			f := str.PopCryptoFrame(1000)
168			Expect(f).ToNot(BeNil())
169			Expect(f.Offset).To(BeZero())
170			Expect(f.Data).To(Equal([]byte("foobar")))
171		})
172
173		It("respects the maximum size", func() {
174			frameHeaderLen := (&wire.CryptoFrame{}).Length(protocol.VersionWhatever)
175			_, err := str.Write([]byte("foobar"))
176			Expect(err).ToNot(HaveOccurred())
177			f := str.PopCryptoFrame(frameHeaderLen + 3)
178			Expect(f).ToNot(BeNil())
179			Expect(f.Offset).To(BeZero())
180			Expect(f.Data).To(Equal([]byte("foo")))
181			f = str.PopCryptoFrame(frameHeaderLen + 3)
182			Expect(f).ToNot(BeNil())
183			Expect(f.Offset).To(Equal(protocol.ByteCount(3)))
184			Expect(f.Data).To(Equal([]byte("bar")))
185		})
186	})
187})
188