add support for diversification to key derivation

ref #51
This commit is contained in:
Lucas Clemente 2016-05-21 00:04:28 +02:00
parent 2606b891e2
commit 241c9f3a3c
4 changed files with 92 additions and 8 deletions

View file

@ -12,7 +12,7 @@ import (
)
// DeriveKeysChacha20 derives the client and server keys and creates a matching chacha20poly1305 instance
func DeriveKeysChacha20(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte) (AEAD, error) {
func DeriveKeysChacha20(version protocol.VersionNumber, forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte) (AEAD, error) {
var info bytes.Buffer
if forwardSecure {
info.Write([]byte("QUIC forward secure key expansion\x00"))
@ -44,5 +44,28 @@ func DeriveKeysChacha20(forwardSecure bool, sharedSecret, nonces []byte, connID
return nil, err
}
if !forwardSecure && version >= protocol.VersionNumber(33) {
if err := diversify(myKey, myIV, divNonce); err != nil {
return nil, err
}
}
return NewAEADChacha20Poly1305(otherKey, myKey, otherIV, myIV)
}
func diversify(key, iv, divNonce []byte) error {
secret := make([]byte, len(key)+len(iv))
copy(secret, key)
copy(secret[len(key):], iv)
r := hkdf.New(sha256.New, secret, divNonce, []byte("QUIC key diversification"))
if _, err := io.ReadFull(r, key); err != nil {
return err
}
if _, err := io.ReadFull(r, iv); err != nil {
return err
}
return nil
}

View file

@ -10,6 +10,7 @@ import (
var _ = Describe("KeyDerivation", func() {
It("derives non-fs keys", func() {
aead, err := DeriveKeysChacha20(
32,
false,
[]byte("0123456789012345678901"),
[]byte("nonce"),
@ -17,6 +18,7 @@ var _ = Describe("KeyDerivation", func() {
[]byte("chlo"),
[]byte("scfg"),
[]byte("cert"),
nil,
)
Expect(err).ToNot(HaveOccurred())
chacha := aead.(*aeadChacha20Poly1305)
@ -27,6 +29,7 @@ var _ = Describe("KeyDerivation", func() {
It("derives fs keys", func() {
aead, err := DeriveKeysChacha20(
32,
true,
[]byte("0123456789012345678901"),
[]byte("nonce"),
@ -34,6 +37,7 @@ var _ = Describe("KeyDerivation", func() {
[]byte("chlo"),
[]byte("scfg"),
[]byte("cert"),
nil,
)
Expect(err).ToNot(HaveOccurred())
chacha := aead.(*aeadChacha20Poly1305)
@ -41,4 +45,42 @@ var _ = Describe("KeyDerivation", func() {
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(
33,
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(
33,
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}))
})
})

View file

@ -13,7 +13,7 @@ import (
)
// KeyDerivationFunction is used for key derivation
type KeyDerivationFunction func(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte) (crypto.AEAD, error)
type KeyDerivationFunction func(version protocol.VersionNumber, forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte) (crypto.AEAD, error)
// KeyExchangeFunction is used to make a new KEX
type KeyExchangeFunction func() (crypto.KeyExchange, error)
@ -206,9 +206,6 @@ func (h *CryptoSetup) handleCHLO(sni string, data []byte, cryptoData map[Tag][]b
if err != nil {
return nil, err
}
var fsNonce bytes.Buffer
fsNonce.Write(cryptoData[TagNONC])
fsNonce.Write(h.nonce)
h.mutex.Lock()
defer h.mutex.Unlock()
@ -218,12 +215,25 @@ func (h *CryptoSetup) handleCHLO(sni string, data []byte, cryptoData map[Tag][]b
return nil, err
}
h.secureAEAD, err = h.keyDerivation(false, sharedSecret, cryptoData[TagNONC], h.connID, data, h.scfg.Get(), certUncompressed)
h.secureAEAD, err = h.keyDerivation(
h.version,
false,
sharedSecret,
cryptoData[TagNONC],
h.connID,
data,
h.scfg.Get(),
certUncompressed,
h.diversificationNonce,
)
if err != nil {
return nil, err
}
// Generate a new curve instance to derive the forward secure key
var fsNonce bytes.Buffer
fsNonce.Write(cryptoData[TagNONC])
fsNonce.Write(h.nonce)
ephermalKex, err := h.keyExchange()
if err != nil {
return nil, err
@ -232,7 +242,16 @@ func (h *CryptoSetup) handleCHLO(sni string, data []byte, cryptoData map[Tag][]b
if err != nil {
return nil, err
}
h.forwardSecureAEAD, err = h.keyDerivation(true, ephermalSharedSecret, fsNonce.Bytes(), h.connID, data, h.scfg.Get(), certUncompressed)
h.forwardSecureAEAD, err = h.keyDerivation(h.version,
true,
ephermalSharedSecret,
fsNonce.Bytes(),
h.connID,
data,
h.scfg.Get(),
certUncompressed,
nil,
)
if err != nil {
return nil, err
}

View file

@ -72,7 +72,7 @@ func (mockAEAD) DiversificationNonce() []byte { return nil }
var expectedInitialNonceLen int
var expectedFSNonceLen int
func mockKeyDerivation(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte) (crypto.AEAD, error) {
func mockKeyDerivation(v protocol.VersionNumber, forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte) (crypto.AEAD, error) {
if forwardSecure {
Expect(nonces).To(HaveLen(expectedFSNonceLen))
} else {