diff --git a/crypto/key_derivation.go b/crypto/key_derivation.go index 60648d8e..470137f7 100644 --- a/crypto/key_derivation.go +++ b/crypto/key_derivation.go @@ -21,15 +21,21 @@ import ( // } // DeriveKeysAESGCM derives the client and server keys and creates a matching AES-GCM AEAD instance -func DeriveKeysAESGCM(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte) (AEAD, error) { - otherKey, myKey, otherIV, myIV, err := deriveKeys(forwardSecure, sharedSecret, nonces, connID, chlo, scfg, cert, divNonce, 16) +func DeriveKeysAESGCM(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte, pers protocol.Perspective) (AEAD, error) { + var swap bool + if pers == protocol.PerspectiveClient { + swap = true + } + otherKey, myKey, otherIV, myIV, err := deriveKeys(forwardSecure, sharedSecret, nonces, connID, chlo, scfg, cert, divNonce, 16, swap) if err != nil { return nil, err } return NewAEADAESGCM(otherKey, myKey, otherIV, myIV) } -func deriveKeys(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo, scfg, cert, divNonce []byte, keyLen int) ([]byte, []byte, []byte, []byte, error) { +// deriveKeys derives the keys and the IVs +// swap should be set true if generating the values for the client, and false for the server +func deriveKeys(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo, scfg, cert, divNonce []byte, keyLen int, swap bool) ([]byte, []byte, []byte, []byte, error) { var info bytes.Buffer if forwardSecure { info.Write([]byte("QUIC forward secure key expansion\x00")) @@ -47,17 +53,33 @@ func deriveKeys(forwardSecure bool, sharedSecret, nonces []byte, connID protocol if _, err := io.ReadFull(r, s); err != nil { return nil, nil, nil, nil, err } - otherKey := s[:keyLen] - myKey := s[keyLen : 2*keyLen] - otherIV := s[2*keyLen : 2*keyLen+4] - myIV := s[2*keyLen+4:] + + key1 := s[:keyLen] + key2 := s[keyLen : 2*keyLen] + iv1 := s[2*keyLen : 2*keyLen+4] + iv2 := s[2*keyLen+4:] + + var otherKey, myKey []byte + var otherIV, myIV []byte if !forwardSecure { - if err := diversify(myKey, myIV, divNonce); err != nil { + if err := diversify(key2, iv2, divNonce); err != nil { return nil, nil, nil, nil, err } } + if swap { + otherKey = key2 + myKey = key1 + otherIV = iv2 + myIV = iv1 + } else { + otherKey = key1 + myKey = key2 + otherIV = iv1 + myIV = iv2 + } + return otherKey, myKey, otherIV, myIV, nil } diff --git a/crypto/key_derivation_test.go b/crypto/key_derivation_test.go index ca6e338d..9c02c987 100644 --- a/crypto/key_derivation_test.go +++ b/crypto/key_derivation_test.go @@ -87,7 +87,7 @@ var _ = Describe("KeyDerivation", func() { // }) Context("AES-GCM", func() { - It("derives non-fs keys", func() { + It("derives non-forward secure keys", func() { aead, err := DeriveKeysAESGCM( false, []byte("0123456789012345678901"), @@ -97,15 +97,66 @@ var _ = Describe("KeyDerivation", func() { []byte("scfg"), []byte("cert"), []byte("divnonce"), + protocol.PerspectiveServer, ) Expect(err).ToNot(HaveOccurred()) - chacha := aead.(*aeadAESGCM) + aesgcm := 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})) + Expect(aesgcm.myIV).To(Equal([]byte{0x1c, 0xec, 0xac, 0x9b})) + Expect(aesgcm.otherIV).To(Equal([]byte{0x64, 0xef, 0x3c, 0x9})) }) - It("derives fs keys", func() { + It("uses the diversification nonce when generating non-forwared secure keys", func() { + aead1, err := DeriveKeysAESGCM( + false, + []byte("0123456789012345678901"), + []byte("nonce"), + protocol.ConnectionID(42), + []byte("chlo"), + []byte("scfg"), + []byte("cert"), + []byte("divnonce"), + protocol.PerspectiveServer, + ) + Expect(err).ToNot(HaveOccurred()) + aead2, err := DeriveKeysAESGCM( + false, + []byte("0123456789012345678901"), + []byte("nonce"), + protocol.ConnectionID(42), + []byte("chlo"), + []byte("scfg"), + []byte("cert"), + []byte("ecnonvid"), + protocol.PerspectiveServer, + ) + Expect(err).ToNot(HaveOccurred()) + aesgcm1 := aead1.(*aeadAESGCM) + aesgcm2 := aead2.(*aeadAESGCM) + Expect(aesgcm1.myIV).ToNot(Equal(aesgcm2.myIV)) + Expect(aesgcm1.otherIV).To(Equal(aesgcm2.otherIV)) + }) + + It("derives non-forward secure keys, for the other side", func() { + aead, err := DeriveKeysAESGCM( + false, + []byte("0123456789012345678901"), + []byte("nonce"), + protocol.ConnectionID(42), + []byte("chlo"), + []byte("scfg"), + []byte("cert"), + []byte("divnonce"), + protocol.PerspectiveClient, + ) + Expect(err).ToNot(HaveOccurred()) + aesgcm := aead.(*aeadAESGCM) + // If the IVs match, the keys will match too, since the keys are read earlier + Expect(aesgcm.otherIV).To(Equal([]byte{0x1c, 0xec, 0xac, 0x9b})) + Expect(aesgcm.myIV).To(Equal([]byte{0x64, 0xef, 0x3c, 0x9})) + }) + + It("derives forward secure keys", func() { aead, err := DeriveKeysAESGCM( true, []byte("0123456789012345678901"), @@ -115,12 +166,13 @@ var _ = Describe("KeyDerivation", func() { []byte("scfg"), []byte("cert"), nil, + protocol.PerspectiveServer, ) Expect(err).ToNot(HaveOccurred()) - chacha := aead.(*aeadAESGCM) + aesgcm := 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})) + Expect(aesgcm.myIV).To(Equal([]byte{0x7, 0xad, 0xab, 0xb8})) + Expect(aesgcm.otherIV).To(Equal([]byte{0xf2, 0x7a, 0xcc, 0x42})) }) It("does not use div-nonce for FS key derivation", func() { @@ -133,12 +185,13 @@ var _ = Describe("KeyDerivation", func() { []byte("scfg"), []byte("cert"), []byte("divnonce"), + protocol.PerspectiveServer, ) Expect(err).ToNot(HaveOccurred()) - chacha := aead.(*aeadAESGCM) + aesgcm := 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})) + Expect(aesgcm.myIV).To(Equal([]byte{0x7, 0xad, 0xab, 0xb8})) + Expect(aesgcm.otherIV).To(Equal([]byte{0xf2, 0x7a, 0xcc, 0x42})) }) }) }) diff --git a/handshake/crypto_setup_server.go b/handshake/crypto_setup_server.go index b8a8d0bb..9d735535 100644 --- a/handshake/crypto_setup_server.go +++ b/handshake/crypto_setup_server.go @@ -15,7 +15,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, divNonce []byte) (crypto.AEAD, error) +type KeyDerivationFunction func(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte, pers protocol.Perspective) (crypto.AEAD, error) // KeyExchangeFunction is used to make a new KEX type KeyExchangeFunction func() crypto.KeyExchange @@ -304,6 +304,7 @@ func (h *cryptoSetupServer) handleCHLO(sni string, data []byte, cryptoData map[T h.scfg.Get(), certUncompressed, h.diversificationNonce, + protocol.PerspectiveServer, ) if err != nil { return nil, err @@ -328,6 +329,7 @@ func (h *cryptoSetupServer) handleCHLO(sni string, data []byte, cryptoData map[T h.scfg.Get(), certUncompressed, nil, + protocol.PerspectiveServer, ) if err != nil { return nil, err diff --git a/handshake/crypto_setup_server_test.go b/handshake/crypto_setup_server_test.go index beb317b8..c26b86e8 100644 --- a/handshake/crypto_setup_server_test.go +++ b/handshake/crypto_setup_server_test.go @@ -82,7 +82,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, divNonce []byte) (crypto.AEAD, error) { +func mockKeyDerivation(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte, pers protocol.Perspective) (crypto.AEAD, error) { if forwardSecure { Expect(nonces).To(HaveLen(expectedFSNonceLen)) } else {