forked from quic-go/quic-go
implement AES-GCM as AEAD
This commit is contained in:
@@ -7,6 +7,6 @@ import (
|
||||
|
||||
// An AEAD implements QUIC's authenticated encryption and associated data
|
||||
type AEAD interface {
|
||||
Open(associatedData []byte, ciphertext io.Reader) (*bytes.Reader, error)
|
||||
Seal(b *bytes.Buffer, associatedData []byte, plaintext []byte)
|
||||
Open(packetNumber uint64, associatedData []byte, ciphertext io.Reader) (*bytes.Reader, error)
|
||||
Seal(packetNumber uint64, b *bytes.Buffer, associatedData []byte, plaintext []byte)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ type NullAEAD struct{}
|
||||
var _ AEAD = &NullAEAD{}
|
||||
|
||||
// Open and verify the ciphertext
|
||||
func (*NullAEAD) Open(associatedData []byte, r io.Reader) (*bytes.Reader, error) {
|
||||
func (*NullAEAD) Open(packetNumber uint64, associatedData []byte, r io.Reader) (*bytes.Reader, error) {
|
||||
ciphertext, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -40,7 +40,7 @@ func (*NullAEAD) Open(associatedData []byte, r io.Reader) (*bytes.Reader, error)
|
||||
}
|
||||
|
||||
// Seal writes hash and ciphertext to the buffer
|
||||
func (*NullAEAD) Seal(b *bytes.Buffer, associatedData []byte, plaintext []byte) {
|
||||
func (*NullAEAD) Seal(packetNumber uint64, b *bytes.Buffer, associatedData []byte, plaintext []byte) {
|
||||
hash := New128a()
|
||||
hash.Write(associatedData)
|
||||
hash.Write(plaintext)
|
||||
|
||||
@@ -15,7 +15,7 @@ var _ = Describe("Crypto/NullAEAD", func() {
|
||||
hash := []byte{0x98, 0x9b, 0x33, 0x3f, 0xe8, 0xde, 0x32, 0x5c, 0xa6, 0x7f, 0x9c, 0xf7}
|
||||
cipherText := append(hash, plainText...)
|
||||
aead := &NullAEAD{}
|
||||
r, err := aead.Open(aad, bytes.NewReader(cipherText))
|
||||
r, err := aead.Open(0, aad, bytes.NewReader(cipherText))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
res, err := ioutil.ReadAll(r)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
@@ -28,7 +28,7 @@ var _ = Describe("Crypto/NullAEAD", func() {
|
||||
hash := []byte{0x98, 0x9b, 0x33, 0x3f, 0xe8, 0xde, 0x32, 0x5c, 0xa6, 0x7f, 0x9c, 0xf7}
|
||||
cipherText := append(hash, plainText...)
|
||||
aead := &NullAEAD{}
|
||||
_, err := aead.Open(aad, bytes.NewReader(cipherText))
|
||||
_, err := aead.Open(0, aad, bytes.NewReader(cipherText))
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
@@ -37,7 +37,7 @@ var _ = Describe("Crypto/NullAEAD", func() {
|
||||
plainText := []byte("They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood.")
|
||||
b := &bytes.Buffer{}
|
||||
aead := &NullAEAD{}
|
||||
aead.Seal(b, aad, plainText)
|
||||
aead.Seal(0, b, aad, plainText)
|
||||
Expect(b.Bytes()).To(Equal(append([]byte{0x98, 0x9b, 0x33, 0x3f, 0xe8, 0xde, 0x32, 0x5c, 0xa6, 0x7f, 0x9c, 0xf7}, []byte("They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood.")...)))
|
||||
})
|
||||
})
|
||||
|
||||
71
crypto/aes_gcm_aead.go
Normal file
71
crypto/aes_gcm_aead.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
type aeadAESGCM struct {
|
||||
otherIV []byte
|
||||
myIV []byte
|
||||
encrypter cipher.AEAD
|
||||
decrypter cipher.AEAD
|
||||
}
|
||||
|
||||
// NewAEADAESGCM creates a AEAD using AES-GCM
|
||||
func NewAEADAESGCM(otherKey []byte, myKey []byte, otherIV []byte, myIV []byte) (AEAD, error) {
|
||||
if len(myKey) != 16 || len(otherKey) != 16 || len(myIV) != 4 || len(otherIV) != 4 {
|
||||
return nil, errors.New("AES-GCM: expected 16-byte keys and 4-byte IVs")
|
||||
}
|
||||
encCipher, err := aes.NewCipher(myKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encrypter, err := cipher.NewGCM(encCipher)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decCipher, err := aes.NewCipher(otherKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decrypter, err := cipher.NewGCM(decCipher)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &aeadAESGCM{
|
||||
otherIV: otherIV,
|
||||
myIV: myIV,
|
||||
encrypter: encrypter,
|
||||
decrypter: decrypter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (aead *aeadAESGCM) Open(packetNumber uint64, associatedData []byte, r io.Reader) (*bytes.Reader, error) {
|
||||
ciphertext, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plaintext, err := aead.decrypter.Open(nil, makeNonce(aead.otherIV, packetNumber), ciphertext, associatedData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bytes.NewReader(plaintext), nil
|
||||
}
|
||||
|
||||
func (aead *aeadAESGCM) Seal(packetNumber uint64, b *bytes.Buffer, associatedData []byte, plaintext []byte) {
|
||||
ciphertext := aead.encrypter.Seal(nil, makeNonce(aead.myIV, packetNumber), plaintext, associatedData)
|
||||
b.Write(ciphertext)
|
||||
}
|
||||
|
||||
func makeNonce(iv []byte, packetNumber uint64) []byte {
|
||||
res := make([]byte, 12)
|
||||
copy(res[0:4], iv)
|
||||
binary.LittleEndian.PutUint64(res[4:12], packetNumber)
|
||||
return res
|
||||
}
|
||||
59
crypto/aes_gcm_aead_test.go
Normal file
59
crypto/aes_gcm_aead_test.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"io/ioutil"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("AES-GCM AEAD", func() {
|
||||
var (
|
||||
alice, bob AEAD
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
keyAlice := make([]byte, 16)
|
||||
keyBob := make([]byte, 16)
|
||||
ivAlice := make([]byte, 4)
|
||||
ivBob := make([]byte, 4)
|
||||
rand.Reader.Read(keyAlice)
|
||||
rand.Reader.Read(keyBob)
|
||||
rand.Reader.Read(ivAlice)
|
||||
rand.Reader.Read(ivBob)
|
||||
var err error
|
||||
alice, err = NewAEADAESGCM(keyBob, keyAlice, ivBob, ivAlice)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
bob, err = NewAEADAESGCM(keyAlice, keyBob, ivAlice, ivBob)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("seals and opens", func() {
|
||||
b := &bytes.Buffer{}
|
||||
alice.Seal(42, b, []byte("aad"), []byte("foobar"))
|
||||
r, err := bob.Open(42, []byte("aad"), b)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
text, err := ioutil.ReadAll(r)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(text).To(Equal([]byte("foobar")))
|
||||
})
|
||||
|
||||
It("seals and opens reverse", func() {
|
||||
b := &bytes.Buffer{}
|
||||
bob.Seal(42, b, []byte("aad"), []byte("foobar"))
|
||||
r, err := alice.Open(42, []byte("aad"), b)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
text, err := ioutil.ReadAll(r)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(text).To(Equal([]byte("foobar")))
|
||||
})
|
||||
|
||||
It("fails with wrong aad", func() {
|
||||
b := &bytes.Buffer{}
|
||||
alice.Seal(42, b, []byte("aad"), []byte("foobar"))
|
||||
_, err := bob.Open(42, []byte("aad2"), b)
|
||||
Expect(err).To(MatchError("cipher: message authentication failed"))
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user