diff --git a/crypto/null_aead.go b/crypto/null_aead_fnv128a.go similarity index 83% rename from crypto/null_aead.go rename to crypto/null_aead_fnv128a.go index d33c90fb5..2f3a31d62 100644 --- a/crypto/null_aead.go +++ b/crypto/null_aead_fnv128a.go @@ -9,23 +9,23 @@ import ( ) // nullAEAD handles not-yet encrypted packets -type nullAEAD struct { +type nullAEADFNV128a struct { perspective protocol.Perspective version protocol.VersionNumber } -var _ AEAD = &nullAEAD{} +var _ AEAD = &nullAEADFNV128a{} // NewNullAEAD creates a NullAEAD func NewNullAEAD(p protocol.Perspective, v protocol.VersionNumber) AEAD { - return &nullAEAD{ + return &nullAEADFNV128a{ perspective: p, version: v, } } // Open and verify the ciphertext -func (n *nullAEAD) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) { +func (n *nullAEADFNV128a) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) { if len(src) < 12 { return nil, errors.New("NullAEAD: ciphertext cannot be less than 12 bytes long") } @@ -52,7 +52,7 @@ func (n *nullAEAD) Open(dst, src []byte, packetNumber protocol.PacketNumber, ass } // Seal writes hash and ciphertext to the buffer -func (n *nullAEAD) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte { +func (n *nullAEADFNV128a) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte { if cap(dst) < 12+len(src) { dst = make([]byte, 12+len(src)) } else { diff --git a/crypto/null_aead_test.go b/crypto/null_aead_fnv128a_test.go similarity index 89% rename from crypto/null_aead_test.go rename to crypto/null_aead_fnv128a_test.go index b327b299f..ab7c88b2e 100644 --- a/crypto/null_aead_test.go +++ b/crypto/null_aead_fnv128a_test.go @@ -6,7 +6,7 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("Crypto/NullAEAD", func() { +var _ = Describe("NullAEAD using FNV128a", func() { aad := []byte("All human beings are born free and equal in dignity and rights.") plainText := []byte("They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood.") hash36 := []byte{0x98, 0x9b, 0x33, 0x3f, 0xe8, 0xde, 0x32, 0x5c, 0xa6, 0x7f, 0x9c, 0xf7} @@ -16,12 +16,12 @@ var _ = Describe("Crypto/NullAEAD", func() { aead := NewNullAEAD(protocol.PerspectiveServer, protocol.Version36) res, err := aead.Open(nil, cipherText, 0, aad) Expect(err).ToNot(HaveOccurred()) - Expect(res).To(Equal([]byte("They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood."))) + Expect(res).To(Equal(plainText)) }) It("seals", func() { aead := NewNullAEAD(protocol.PerspectiveServer, protocol.Version36) - Expect(aead.Seal(nil, plainText, 0, aad)).To(Equal(append(hash36, []byte("They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood.")...))) + Expect(aead.Seal(nil, plainText, 0, aad)).To(Equal(append(hash36, plainText...))) }) It("rejects short ciphertexts", func() { diff --git a/crypto/null_aead_fnv64a.go b/crypto/null_aead_fnv64a.go new file mode 100644 index 000000000..07ade19a0 --- /dev/null +++ b/crypto/null_aead_fnv64a.go @@ -0,0 +1,46 @@ +package crypto + +import ( + "encoding/binary" + "errors" + "hash/fnv" + + "github.com/lucas-clemente/quic-go/internal/protocol" +) + +type nullAEADFNV64a struct{} + +var _ AEAD = &nullAEADFNV64a{} + +// Open and verify the ciphertext +func (n *nullAEADFNV64a) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) { + if len(src) < 8 { + return nil, errors.New("NullAEAD: ciphertext cannot be less than 8 bytes long") + } + data := src[:len(src)-8] + + hash := fnv.New64a() + hash.Write(associatedData) + hash.Write(data) + + if hash.Sum64() != binary.BigEndian.Uint64(src[len(src)-8:]) { + return nil, errors.New("NullAEAD: failed to authenticate received data") + } + return data, nil +} + +// Seal writes hash and ciphertext to the buffer +func (n *nullAEADFNV64a) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte { + if cap(dst) < 8+len(src) { + dst = make([]byte, 8+len(src)) + } else { + dst = dst[:8+len(src)] + } + + hash := fnv.New64a() + hash.Write(associatedData) + hash.Write(src) + copy(dst, src) + binary.BigEndian.PutUint64(dst[len(src):], hash.Sum64()) + return dst +} diff --git a/crypto/null_aead_fnv64a_test.go b/crypto/null_aead_fnv64a_test.go new file mode 100644 index 000000000..16b63390e --- /dev/null +++ b/crypto/null_aead_fnv64a_test.go @@ -0,0 +1,64 @@ +package crypto + +import ( + "encoding/binary" + "hash/fnv" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("NullAEAD using FNV128a", func() { + var hash64 []byte + var aead AEAD + aad := []byte("All human beings are born free and equal in dignity and rights.") + plainText := []byte("They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood.") + + BeforeEach(func() { + aead = &nullAEADFNV64a{} + hash := fnv.New64a() + hash.Write(aad) + hash.Write(plainText) + hash64 = make([]byte, 8) + binary.BigEndian.PutUint64(hash64, hash.Sum64()) + }) + + It("opens", func() { + data, err := aead.Open(nil, append(plainText, hash64...), 0, aad) + Expect(err).ToNot(HaveOccurred()) + Expect(data).To(Equal(plainText)) + }) + + It("fails", func() { + _, err := aead.Open(nil, append(plainText, hash64...), 0, append(aad, []byte{0x42}...)) + Expect(err).To(MatchError("NullAEAD: failed to authenticate received data")) + }) + + It("rejects short ciphertexts", func() { + _, err := aead.Open(nil, []byte{1, 2, 3, 4, 5, 6, 7}, 0, []byte{}) + Expect(err).To(MatchError("NullAEAD: ciphertext cannot be less than 8 bytes long")) + }) + + It("opens empty messages", func() { + hash := fnv.New64a() + h := make([]byte, 8) + binary.BigEndian.PutUint64(h, hash.Sum64()) + data, err := aead.Open(nil, h, 0, []byte{}) + Expect(err).ToNot(HaveOccurred()) + Expect(data).To(BeEmpty()) + }) + + It("seals", func() { + Expect(aead.Seal(nil, plainText, 0, aad)).To(Equal(append(plainText, hash64...))) + }) + + It("seals in-place", func() { + buf := make([]byte, 6, 6+8) + copy(buf, []byte("foobar")) + res := aead.Seal(buf[0:0], buf, 0, nil) + // buf = buf[:8+6] + Expect(buf[:6]).To(Equal([]byte("foobar"))) + // Expect(res[:6]).To(Equal([]byte("foobar"))) + Expect(buf[0 : 6+8]).To(Equal(res)) + }) +})