package handshake import ( "bytes" "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/tls" "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Long Header AEAD", func() { for _, ver := range []protocol.VersionNumber{protocol.VersionDraft29, protocol.Version1, protocol.Version2} { v := ver Context(fmt.Sprintf("using version %s", v), func() { for i := range cipherSuites { cs := cipherSuites[i] Context(fmt.Sprintf("using %s", tls.CipherSuiteName(cs.ID)), func() { getSealerAndOpener := func() (LongHeaderSealer, LongHeaderOpener) { key := make([]byte, 16) hpKey := make([]byte, 16) rand.Read(key) rand.Read(hpKey) block, err := aes.NewCipher(key) Expect(err).ToNot(HaveOccurred()) aead, err := cipher.NewGCM(block) Expect(err).ToNot(HaveOccurred()) return newLongHeaderSealer(aead, newHeaderProtector(cs, hpKey, true, v)), newLongHeaderOpener(aead, newHeaderProtector(cs, hpKey, true, v)) } Context("message encryption", func() { msg := []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.") ad := []byte("Donec in velit neque.") It("encrypts and decrypts a message", func() { sealer, opener := getSealerAndOpener() encrypted := sealer.Seal(nil, msg, 0x1337, ad) opened, err := opener.Open(nil, encrypted, 0x1337, ad) Expect(err).ToNot(HaveOccurred()) Expect(opened).To(Equal(msg)) }) It("fails to open a message if the associated data is not the same", func() { sealer, opener := getSealerAndOpener() encrypted := sealer.Seal(nil, msg, 0x1337, ad) _, err := opener.Open(nil, encrypted, 0x1337, []byte("wrong ad")) Expect(err).To(MatchError(ErrDecryptionFailed)) }) It("fails to open a message if the packet number is not the same", func() { sealer, opener := getSealerAndOpener() encrypted := sealer.Seal(nil, msg, 0x1337, ad) _, err := opener.Open(nil, encrypted, 0x42, ad) Expect(err).To(MatchError(ErrDecryptionFailed)) }) It("decodes the packet number", func() { sealer, opener := getSealerAndOpener() encrypted := sealer.Seal(nil, msg, 0x1337, ad) _, err := opener.Open(nil, encrypted, 0x1337, ad) Expect(err).ToNot(HaveOccurred()) Expect(opener.DecodePacketNumber(0x38, protocol.PacketNumberLen1)).To(BeEquivalentTo(0x1338)) }) It("ignores packets it can't decrypt for packet number derivation", func() { sealer, opener := getSealerAndOpener() encrypted := sealer.Seal(nil, msg, 0x1337, ad) _, err := opener.Open(nil, encrypted[:len(encrypted)-1], 0x1337, ad) Expect(err).To(HaveOccurred()) Expect(opener.DecodePacketNumber(0x38, protocol.PacketNumberLen1)).To(BeEquivalentTo(0x38)) }) }) Context("header encryption", func() { It("encrypts and encrypts the header", func() { sealer, opener := getSealerAndOpener() var lastFourBitsDifferent int for i := 0; i < 100; i++ { sample := make([]byte, 16) rand.Read(sample) header := []byte{0xb5, 1, 2, 3, 4, 5, 6, 7, 8, 0xde, 0xad, 0xbe, 0xef} sealer.EncryptHeader(sample, &header[0], header[9:13]) if header[0]&0xf != 0xb5&0xf { lastFourBitsDifferent++ } Expect(header[0] & 0xf0).To(Equal(byte(0xb5 & 0xf0))) Expect(header[1:9]).To(Equal([]byte{1, 2, 3, 4, 5, 6, 7, 8})) Expect(header[9:13]).ToNot(Equal([]byte{0xde, 0xad, 0xbe, 0xef})) opener.DecryptHeader(sample, &header[0], header[9:13]) Expect(header).To(Equal([]byte{0xb5, 1, 2, 3, 4, 5, 6, 7, 8, 0xde, 0xad, 0xbe, 0xef})) } Expect(lastFourBitsDifferent).To(BeNumerically(">", 75)) }) It("encrypts and encrypts the header, for a 0xfff..fff sample", func() { sealer, opener := getSealerAndOpener() var lastFourBitsDifferent int for i := 0; i < 100; i++ { sample := bytes.Repeat([]byte{0xff}, 16) header := []byte{0xb5, 1, 2, 3, 4, 5, 6, 7, 8, 0xde, 0xad, 0xbe, 0xef} sealer.EncryptHeader(sample, &header[0], header[9:13]) if header[0]&0xf != 0xb5&0xf { lastFourBitsDifferent++ } Expect(header[0] & 0xf0).To(Equal(byte(0xb5 & 0xf0))) Expect(header[1:9]).To(Equal([]byte{1, 2, 3, 4, 5, 6, 7, 8})) Expect(header[9:13]).ToNot(Equal([]byte{0xde, 0xad, 0xbe, 0xef})) opener.DecryptHeader(sample, &header[0], header[9:13]) Expect(header).To(Equal([]byte{0xb5, 1, 2, 3, 4, 5, 6, 7, 8, 0xde, 0xad, 0xbe, 0xef})) } }) It("fails to decrypt the header when using a different sample", func() { sealer, opener := getSealerAndOpener() header := []byte{0xb5, 1, 2, 3, 4, 5, 6, 7, 8, 0xde, 0xad, 0xbe, 0xef} sample := make([]byte, 16) rand.Read(sample) sealer.EncryptHeader(sample, &header[0], header[9:13]) rand.Read(sample) // use a different sample opener.DecryptHeader(sample, &header[0], header[9:13]) Expect(header).ToNot(Equal([]byte{0xb5, 1, 2, 3, 4, 5, 6, 7, 8, 0xde, 0xad, 0xbe, 0xef})) }) }) }) } }) Describe("Long Header AEAD", func() { var ( dropped chan struct{} // use a chan because closing it twice will panic aead cipher.AEAD hp headerProtector ) dropCb := func() { close(dropped) } msg := []byte("Lorem ipsum dolor sit amet.") ad := []byte("Donec in velit neque.") BeforeEach(func() { dropped = make(chan struct{}) key := make([]byte, 16) hpKey := make([]byte, 16) rand.Read(key) rand.Read(hpKey) block, err := aes.NewCipher(key) Expect(err).ToNot(HaveOccurred()) aead, err = cipher.NewGCM(block) Expect(err).ToNot(HaveOccurred()) hp = newHeaderProtector(cipherSuites[0], hpKey, true, protocol.Version1) }) Context("for the server", func() { It("drops keys when first successfully processing a Handshake packet", func() { serverOpener := newHandshakeOpener(aead, hp, dropCb, protocol.PerspectiveServer) // first try to open an invalid message _, err := serverOpener.Open(nil, []byte("invalid"), 0, []byte("invalid")) Expect(err).To(HaveOccurred()) Expect(dropped).ToNot(BeClosed()) // then open a valid message enc := newLongHeaderSealer(aead, hp).Seal(nil, msg, 10, ad) _, err = serverOpener.Open(nil, enc, 10, ad) Expect(err).ToNot(HaveOccurred()) Expect(dropped).To(BeClosed()) // now open the same message again to make sure the callback is only called once _, err = serverOpener.Open(nil, enc, 10, ad) Expect(err).ToNot(HaveOccurred()) }) It("doesn't drop keys when sealing a Handshake packet", func() { serverSealer := newHandshakeSealer(aead, hp, dropCb, protocol.PerspectiveServer) serverSealer.Seal(nil, msg, 1, ad) Expect(dropped).ToNot(BeClosed()) }) }) Context("for the client", func() { It("drops keys when first sealing a Handshake packet", func() { clientSealer := newHandshakeSealer(aead, hp, dropCb, protocol.PerspectiveClient) // seal the first message clientSealer.Seal(nil, msg, 1, ad) Expect(dropped).To(BeClosed()) // seal another message to make sure the callback is only called once clientSealer.Seal(nil, msg, 2, ad) }) It("doesn't drop keys when processing a Handshake packet", func() { enc := newLongHeaderSealer(aead, hp).Seal(nil, msg, 42, ad) clientOpener := newHandshakeOpener(aead, hp, dropCb, protocol.PerspectiveClient) _, err := clientOpener.Open(nil, enc, 42, ad) Expect(err).ToNot(HaveOccurred()) Expect(dropped).ToNot(BeClosed()) }) }) }) } })