uquic/internal/handshake/aead_test.go
Marten Seemann 95334202c5 fix decoding of packet numbers in different packet number spaces
When QUIC abandoned the "stream 0" design, it introduced separate
packet number spaces for packets with different encryption levels.
Packet number compression now also works per packet number space.
The current code doesn't lead to any problems if the peer starts sending
with packet number 0, as we only exchange a few packets in the Initial
and the Handshake packet number space and there's nothing to compress.
It might lead to problems if the peer starts with a large packet number
in one space (which is allowed by the spec), and then starts with a
small packet number in another packet number space.
2020-12-03 23:35:27 +07:00

198 lines
7.3 KiB
Go

package handshake
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/qtls"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Long Header AEAD", func() {
for i := range cipherSuites {
cs := cipherSuites[i]
Context(fmt.Sprintf("using %s", qtls.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)),
newLongHeaderOpener(aead, newHeaderProtector(cs, hpKey, true))
}
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}))
})
})
})
}
})
var _ = 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)
})
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())
})
})
})