implement AES-GCM as AEAD

This commit is contained in:
Lucas Clemente
2016-04-14 09:56:39 +02:00
parent 3588f24ad4
commit 8b7e2744da
6 changed files with 139 additions and 9 deletions

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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
View 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
}

View 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"))
})
})