forked from quic-go/quic-go
58
crypto/aesgcm_aead.go
Normal file
58
crypto/aesgcm_aead.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"errors"
|
||||
|
||||
"github.com/lucas-clemente/aes12"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
)
|
||||
|
||||
type aeadAESGCM struct {
|
||||
otherIV []byte
|
||||
myIV []byte
|
||||
encrypter cipher.AEAD
|
||||
decrypter cipher.AEAD
|
||||
}
|
||||
|
||||
// NewAEADAESGCM creates a AEAD using AES-GCM with 12 bytes tag size
|
||||
//
|
||||
// AES-GCM support is a bit hacky, since the go stdlib does not support 12 byte
|
||||
// tag size, and couples the cipher and aes packages closely.
|
||||
// See https://github.com/lucas-clemente/aes12.
|
||||
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")
|
||||
}
|
||||
encrypterCipher, err := aes12.NewCipher(myKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encrypter, err := aes12.NewGCM(encrypterCipher)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decrypterCipher, err := aes12.NewCipher(otherKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decrypter, err := aes12.NewGCM(decrypterCipher)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &aeadAESGCM{
|
||||
otherIV: otherIV,
|
||||
myIV: myIV,
|
||||
encrypter: encrypter,
|
||||
decrypter: decrypter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (aead *aeadAESGCM) Open(packetNumber protocol.PacketNumber, associatedData []byte, ciphertext []byte) ([]byte, error) {
|
||||
return aead.decrypter.Open(nil, makeNonce(aead.otherIV, packetNumber), ciphertext, associatedData)
|
||||
}
|
||||
|
||||
func (aead *aeadAESGCM) Seal(packetNumber protocol.PacketNumber, associatedData []byte, plaintext []byte) []byte {
|
||||
return aead.encrypter.Seal(nil, makeNonce(aead.myIV, packetNumber), plaintext, associatedData)
|
||||
}
|
||||
69
crypto/aesgcm_aead_test.go
Normal file
69
crypto/aesgcm_aead_test.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("AES-GCM", func() {
|
||||
var (
|
||||
alice, bob AEAD
|
||||
keyAlice, keyBob, ivAlice, ivBob []byte
|
||||
)
|
||||
|
||||
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 := alice.Seal(42, []byte("aad"), []byte("foobar"))
|
||||
text, err := bob.Open(42, []byte("aad"), b)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(text).To(Equal([]byte("foobar")))
|
||||
})
|
||||
|
||||
It("seals and opens reverse", func() {
|
||||
b := bob.Seal(42, []byte("aad"), []byte("foobar"))
|
||||
text, err := alice.Open(42, []byte("aad"), b)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(text).To(Equal([]byte("foobar")))
|
||||
})
|
||||
|
||||
It("has the proper length", func() {
|
||||
b := bob.Seal(42, []byte("aad"), []byte("foobar"))
|
||||
Expect(b).To(HaveLen(6 + 12))
|
||||
})
|
||||
|
||||
It("fails with wrong aad", func() {
|
||||
b := alice.Seal(42, []byte("aad"), []byte("foobar"))
|
||||
_, err := bob.Open(42, []byte("aad2"), b)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
It("rejects wrong key and iv sizes", func() {
|
||||
var err error
|
||||
e := "AES-GCM: expected 16-byte keys and 4-byte IVs"
|
||||
_, err = NewAEADAESGCM(keyBob[1:], keyAlice, ivBob, ivAlice)
|
||||
Expect(err).To(MatchError(e))
|
||||
_, err = NewAEADAESGCM(keyBob, keyAlice[1:], ivBob, ivAlice)
|
||||
Expect(err).To(MatchError(e))
|
||||
_, err = NewAEADAESGCM(keyBob, keyAlice, ivBob[1:], ivAlice)
|
||||
Expect(err).To(MatchError(e))
|
||||
_, err = NewAEADAESGCM(keyBob, keyAlice, ivBob, ivAlice[1:])
|
||||
Expect(err).To(MatchError(e))
|
||||
})
|
||||
})
|
||||
@@ -39,11 +39,7 @@ func NewAEADChacha20Poly1305(otherKey []byte, myKey []byte, otherIV []byte, myIV
|
||||
}
|
||||
|
||||
func (aead *aeadChacha20Poly1305) Open(packetNumber protocol.PacketNumber, associatedData []byte, ciphertext []byte) ([]byte, error) {
|
||||
plaintext, err := aead.decrypter.Open(nil, makeNonce(aead.otherIV, packetNumber), ciphertext, associatedData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plaintext, nil
|
||||
return aead.decrypter.Open(nil, makeNonce(aead.otherIV, packetNumber), ciphertext, associatedData)
|
||||
}
|
||||
|
||||
func (aead *aeadChacha20Poly1305) Seal(packetNumber protocol.PacketNumber, associatedData []byte, plaintext []byte) []byte {
|
||||
|
||||
@@ -43,6 +43,11 @@ var _ = Describe("Chacha20poly1305", func() {
|
||||
Expect(text).To(Equal([]byte("foobar")))
|
||||
})
|
||||
|
||||
It("has the proper length", func() {
|
||||
b := bob.Seal(42, []byte("aad"), []byte("foobar"))
|
||||
Expect(b).To(HaveLen(6 + 12))
|
||||
})
|
||||
|
||||
It("fails with wrong aad", func() {
|
||||
b := alice.Seal(42, []byte("aad"), []byte("foobar"))
|
||||
_, err := bob.Open(42, []byte("aad2"), b)
|
||||
|
||||
@@ -11,8 +11,25 @@ import (
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
// DeriveKeysChacha20 derives the client and server keys and creates a matching chacha20poly1305 instance
|
||||
// DeriveKeysChacha20 derives the client and server keys and creates a matching chacha20poly1305 AEAD instance
|
||||
func DeriveKeysChacha20(version protocol.VersionNumber, forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte) (AEAD, error) {
|
||||
otherKey, myKey, otherIV, myIV, err := deriveKeys(version, forwardSecure, sharedSecret, nonces, connID, chlo, scfg, cert, divNonce, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewAEADChacha20Poly1305(otherKey, myKey, otherIV, myIV)
|
||||
}
|
||||
|
||||
// DeriveKeysAESGCM derives the client and server keys and creates a matching AES-GCM AEAD instance
|
||||
func DeriveKeysAESGCM(version protocol.VersionNumber, forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte) (AEAD, error) {
|
||||
otherKey, myKey, otherIV, myIV, err := deriveKeys(version, forwardSecure, sharedSecret, nonces, connID, chlo, scfg, cert, divNonce, 16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewAEADAESGCM(otherKey, myKey, otherIV, myIV)
|
||||
}
|
||||
|
||||
func deriveKeys(version protocol.VersionNumber, forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo, scfg, cert, divNonce []byte, keyLen int) ([]byte, []byte, []byte, []byte, error) {
|
||||
var info bytes.Buffer
|
||||
if forwardSecure {
|
||||
info.Write([]byte("QUIC forward secure key expansion\x00"))
|
||||
@@ -26,31 +43,31 @@ func DeriveKeysChacha20(version protocol.VersionNumber, forwardSecure bool, shar
|
||||
|
||||
r := hkdf.New(sha256.New, sharedSecret, nonces, info.Bytes())
|
||||
|
||||
otherKey := make([]byte, 32)
|
||||
myKey := make([]byte, 32)
|
||||
otherKey := make([]byte, keyLen)
|
||||
myKey := make([]byte, keyLen)
|
||||
otherIV := make([]byte, 4)
|
||||
myIV := make([]byte, 4)
|
||||
|
||||
if _, err := io.ReadFull(r, otherKey); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
if _, err := io.ReadFull(r, myKey); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
if _, err := io.ReadFull(r, otherIV); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
if _, err := io.ReadFull(r, myIV); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
if !forwardSecure && version >= protocol.Version33 {
|
||||
if err := diversify(myKey, myIV, divNonce); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return NewAEADChacha20Poly1305(otherKey, myKey, otherIV, myIV)
|
||||
return otherKey, myKey, otherIV, myIV, nil
|
||||
}
|
||||
|
||||
func diversify(key, iv, divNonce []byte) error {
|
||||
|
||||
@@ -8,79 +8,159 @@ import (
|
||||
)
|
||||
|
||||
var _ = Describe("KeyDerivation", func() {
|
||||
It("derives non-fs keys", func() {
|
||||
aead, err := DeriveKeysChacha20(
|
||||
protocol.Version32,
|
||||
false,
|
||||
[]byte("0123456789012345678901"),
|
||||
[]byte("nonce"),
|
||||
protocol.ConnectionID(42),
|
||||
[]byte("chlo"),
|
||||
[]byte("scfg"),
|
||||
[]byte("cert"),
|
||||
nil,
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
chacha := aead.(*aeadChacha20Poly1305)
|
||||
// If the IVs match, the keys will match too, since the keys are read earlier
|
||||
Expect(chacha.myIV).To(Equal([]byte{0xf0, 0xf5, 0x4c, 0xa8}))
|
||||
Expect(chacha.otherIV).To(Equal([]byte{0x75, 0xd8, 0xa2, 0x8d}))
|
||||
Context("chacha20poly1305", func() {
|
||||
It("derives non-fs keys", func() {
|
||||
aead, err := DeriveKeysChacha20(
|
||||
protocol.Version32,
|
||||
false,
|
||||
[]byte("0123456789012345678901"),
|
||||
[]byte("nonce"),
|
||||
protocol.ConnectionID(42),
|
||||
[]byte("chlo"),
|
||||
[]byte("scfg"),
|
||||
[]byte("cert"),
|
||||
nil,
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
chacha := aead.(*aeadChacha20Poly1305)
|
||||
// If the IVs match, the keys will match too, since the keys are read earlier
|
||||
Expect(chacha.myIV).To(Equal([]byte{0xf0, 0xf5, 0x4c, 0xa8}))
|
||||
Expect(chacha.otherIV).To(Equal([]byte{0x75, 0xd8, 0xa2, 0x8d}))
|
||||
})
|
||||
|
||||
It("derives fs keys", func() {
|
||||
aead, err := DeriveKeysChacha20(
|
||||
protocol.Version32,
|
||||
true,
|
||||
[]byte("0123456789012345678901"),
|
||||
[]byte("nonce"),
|
||||
protocol.ConnectionID(42),
|
||||
[]byte("chlo"),
|
||||
[]byte("scfg"),
|
||||
[]byte("cert"),
|
||||
nil,
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
chacha := aead.(*aeadChacha20Poly1305)
|
||||
// If the IVs match, the keys will match too, since the keys are read earlier
|
||||
Expect(chacha.myIV).To(Equal([]byte{0xf5, 0x73, 0x11, 0x79}))
|
||||
Expect(chacha.otherIV).To(Equal([]byte{0xf7, 0x26, 0x4d, 0x2c}))
|
||||
})
|
||||
|
||||
It("does not use diversification nonces in FS key derivation", func() {
|
||||
aead, err := DeriveKeysChacha20(
|
||||
protocol.Version33,
|
||||
true,
|
||||
[]byte("0123456789012345678901"),
|
||||
[]byte("nonce"),
|
||||
protocol.ConnectionID(42),
|
||||
[]byte("chlo"),
|
||||
[]byte("scfg"),
|
||||
[]byte("cert"),
|
||||
[]byte("divnonce"),
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
chacha := aead.(*aeadChacha20Poly1305)
|
||||
// If the IVs match, the keys will match too, since the keys are read earlier
|
||||
Expect(chacha.myIV).To(Equal([]byte{0xf5, 0x73, 0x11, 0x79}))
|
||||
Expect(chacha.otherIV).To(Equal([]byte{0xf7, 0x26, 0x4d, 0x2c}))
|
||||
})
|
||||
|
||||
It("uses diversification nonces in initial key derivation", func() {
|
||||
aead, err := DeriveKeysChacha20(
|
||||
protocol.Version33,
|
||||
false,
|
||||
[]byte("0123456789012345678901"),
|
||||
[]byte("nonce"),
|
||||
protocol.ConnectionID(42),
|
||||
[]byte("chlo"),
|
||||
[]byte("scfg"),
|
||||
[]byte("cert"),
|
||||
[]byte("divnonce"),
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
chacha := aead.(*aeadChacha20Poly1305)
|
||||
// If the IVs match, the keys will match too, since the keys are read earlier
|
||||
Expect(chacha.myIV).To(Equal([]byte{0xc4, 0x12, 0x25, 0x64}))
|
||||
Expect(chacha.otherIV).To(Equal([]byte{0x75, 0xd8, 0xa2, 0x8d}))
|
||||
})
|
||||
})
|
||||
|
||||
It("derives fs keys", func() {
|
||||
aead, err := DeriveKeysChacha20(
|
||||
protocol.Version32,
|
||||
true,
|
||||
[]byte("0123456789012345678901"),
|
||||
[]byte("nonce"),
|
||||
protocol.ConnectionID(42),
|
||||
[]byte("chlo"),
|
||||
[]byte("scfg"),
|
||||
[]byte("cert"),
|
||||
nil,
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
chacha := aead.(*aeadChacha20Poly1305)
|
||||
// If the IVs match, the keys will match too, since the keys are read earlier
|
||||
Expect(chacha.myIV).To(Equal([]byte{0xf5, 0x73, 0x11, 0x79}))
|
||||
Expect(chacha.otherIV).To(Equal([]byte{0xf7, 0x26, 0x4d, 0x2c}))
|
||||
})
|
||||
Context("AES-GCM", func() {
|
||||
It("derives non-fs keys", func() {
|
||||
aead, err := DeriveKeysAESGCM(
|
||||
protocol.Version32,
|
||||
false,
|
||||
[]byte("0123456789012345678901"),
|
||||
[]byte("nonce"),
|
||||
protocol.ConnectionID(42),
|
||||
[]byte("chlo"),
|
||||
[]byte("scfg"),
|
||||
[]byte("cert"),
|
||||
nil,
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
chacha := aead.(*aeadAESGCM)
|
||||
// If the IVs match, the keys will match too, since the keys are read earlier
|
||||
Expect(chacha.myIV).To(Equal([]byte{0x28, 0x71, 0x71, 0x16}))
|
||||
Expect(chacha.otherIV).To(Equal([]byte{0x64, 0xef, 0x3c, 0x9}))
|
||||
})
|
||||
|
||||
It("does not use diversification nonces in FS key derivation", func() {
|
||||
aead, err := DeriveKeysChacha20(
|
||||
protocol.Version33,
|
||||
true,
|
||||
[]byte("0123456789012345678901"),
|
||||
[]byte("nonce"),
|
||||
protocol.ConnectionID(42),
|
||||
[]byte("chlo"),
|
||||
[]byte("scfg"),
|
||||
[]byte("cert"),
|
||||
[]byte("divnonce"),
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
chacha := aead.(*aeadChacha20Poly1305)
|
||||
// If the IVs match, the keys will match too, since the keys are read earlier
|
||||
Expect(chacha.myIV).To(Equal([]byte{0xf5, 0x73, 0x11, 0x79}))
|
||||
Expect(chacha.otherIV).To(Equal([]byte{0xf7, 0x26, 0x4d, 0x2c}))
|
||||
})
|
||||
It("derives fs keys", func() {
|
||||
aead, err := DeriveKeysAESGCM(
|
||||
protocol.Version32,
|
||||
true,
|
||||
[]byte("0123456789012345678901"),
|
||||
[]byte("nonce"),
|
||||
protocol.ConnectionID(42),
|
||||
[]byte("chlo"),
|
||||
[]byte("scfg"),
|
||||
[]byte("cert"),
|
||||
nil,
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
chacha := aead.(*aeadAESGCM)
|
||||
// If the IVs match, the keys will match too, since the keys are read earlier
|
||||
Expect(chacha.myIV).To(Equal([]byte{0x7, 0xad, 0xab, 0xb8}))
|
||||
Expect(chacha.otherIV).To(Equal([]byte{0xf2, 0x7a, 0xcc, 0x42}))
|
||||
})
|
||||
|
||||
It("uses diversification nonces in initial key derivation", func() {
|
||||
aead, err := DeriveKeysChacha20(
|
||||
protocol.Version33,
|
||||
false,
|
||||
[]byte("0123456789012345678901"),
|
||||
[]byte("nonce"),
|
||||
protocol.ConnectionID(42),
|
||||
[]byte("chlo"),
|
||||
[]byte("scfg"),
|
||||
[]byte("cert"),
|
||||
[]byte("divnonce"),
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
chacha := aead.(*aeadChacha20Poly1305)
|
||||
// If the IVs match, the keys will match too, since the keys are read earlier
|
||||
Expect(chacha.myIV).To(Equal([]byte{0xc4, 0x12, 0x25, 0x64}))
|
||||
Expect(chacha.otherIV).To(Equal([]byte{0x75, 0xd8, 0xa2, 0x8d}))
|
||||
It("does not use diversification nonces in FS key derivation", func() {
|
||||
aead, err := DeriveKeysAESGCM(
|
||||
protocol.Version33,
|
||||
true,
|
||||
[]byte("0123456789012345678901"),
|
||||
[]byte("nonce"),
|
||||
protocol.ConnectionID(42),
|
||||
[]byte("chlo"),
|
||||
[]byte("scfg"),
|
||||
[]byte("cert"),
|
||||
[]byte("divnonce"),
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
chacha := aead.(*aeadAESGCM)
|
||||
// If the IVs match, the keys will match too, since the keys are read earlier
|
||||
Expect(chacha.myIV).To(Equal([]byte{0x7, 0xad, 0xab, 0xb8}))
|
||||
Expect(chacha.otherIV).To(Equal([]byte{0xf2, 0x7a, 0xcc, 0x42}))
|
||||
})
|
||||
|
||||
It("uses diversification nonces in initial key derivation", func() {
|
||||
aead, err := DeriveKeysAESGCM(
|
||||
protocol.Version33,
|
||||
false,
|
||||
[]byte("0123456789012345678901"),
|
||||
[]byte("nonce"),
|
||||
protocol.ConnectionID(42),
|
||||
[]byte("chlo"),
|
||||
[]byte("scfg"),
|
||||
[]byte("cert"),
|
||||
[]byte("divnonce"),
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
chacha := aead.(*aeadAESGCM)
|
||||
// If the IVs match, the keys will match too, since the keys are read earlier
|
||||
Expect(chacha.myIV).To(Equal([]byte{0x1c, 0xec, 0xac, 0x9b}))
|
||||
Expect(chacha.otherIV).To(Equal([]byte{0x64, 0xef, 0x3c, 0x9}))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user