diff --git a/handshake/crypto_setup.go b/handshake/crypto_setup.go index a0515d00b..98c7080a8 100644 --- a/handshake/crypto_setup.go +++ b/handshake/crypto_setup.go @@ -248,8 +248,9 @@ func (h *CryptoSetup) handleCHLO(sni string, data []byte, cryptoData map[Tag][]b } clientNonce := cryptoData[TagNONC] - if len(clientNonce) != 32 { - return nil, qerr.Error(qerr.InvalidCryptoMessageParameter, "invalid client nonce length") + err = h.validateClientNonce(clientNonce) + if err != nil { + return nil, err } h.secureAEAD, err = h.keyDerivation( @@ -331,3 +332,13 @@ func (h *CryptoSetup) UnlockForSealing() { func (h *CryptoSetup) HandshakeComplete() bool { return h.receivedForwardSecurePacket } + +func (h *CryptoSetup) validateClientNonce(nonce []byte) error { + if len(nonce) != 32 { + return qerr.Error(qerr.InvalidCryptoMessageParameter, "invalid client nonce length") + } + if !bytes.Equal(nonce[4:12], h.scfg.obit) { + return qerr.Error(qerr.InvalidCryptoMessageParameter, "OBIT not matching") + } + return nil +} diff --git a/handshake/crypto_setup_test.go b/handshake/crypto_setup_test.go index e93e9aaa3..9af35a2bf 100644 --- a/handshake/crypto_setup_test.go +++ b/handshake/crypto_setup_test.go @@ -150,7 +150,6 @@ var _ = Describe("Crypto setup", func() { ip = net.ParseIP("1.2.3.4") validSTK, err = mockStkSource{}.NewToken(ip) Expect(err).NotTo(HaveOccurred()) - nonce32 = make([]byte, 32) expectedInitialNonceLen = 32 expectedFSNonceLen = 64 aeadChanged = make(chan struct{}, 1) @@ -158,6 +157,8 @@ var _ = Describe("Crypto setup", func() { kex = &mockKEX{} signer = &mockSigner{} scfg, err = NewServerConfig(kex, signer) + nonce32 = make([]byte, 32) + copy(nonce32[4:12], scfg.obit) // set the OBIT value at the right position Expect(err).NotTo(HaveOccurred()) scfg.stkSource = &mockStkSource{} v := protocol.SupportedVersions[len(protocol.SupportedVersions)-1] @@ -274,6 +275,19 @@ var _ = Describe("Crypto setup", func() { Expect(err).To(MatchError(qerr.Error(qerr.InvalidCryptoMessageParameter, "invalid client nonce length"))) }) + It("rejects client nonces that have the wrong OBIT value", func() { + nonce := make([]byte, 32) // the OBIT value is nonce[4:12] and here just initialized to 0 + WriteHandshakeMessage(&stream.dataToRead, TagCHLO, map[Tag][]byte{ + TagSCID: scfg.ID, + TagSNI: []byte("quic.clemente.io"), + TagNONC: nonce, + TagSTK: validSTK, + TagPUBS: nil, + }) + err := cs.HandleCryptoStream() + Expect(err).To(MatchError(qerr.Error(qerr.InvalidCryptoMessageParameter, "OBIT not matching"))) + }) + It("handles 0-RTT handshake", func() { WriteHandshakeMessage(&stream.dataToRead, TagCHLO, map[Tag][]byte{ TagSCID: scfg.ID, diff --git a/handshake/server_config.go b/handshake/server_config.go index dc33e9775..1a8371d41 100644 --- a/handshake/server_config.go +++ b/handshake/server_config.go @@ -9,9 +9,10 @@ import ( // ServerConfig is a server config type ServerConfig struct { + ID []byte + obit []byte kex crypto.KeyExchange signer crypto.Signer - ID []byte stkSource crypto.StkSource } @@ -33,9 +34,10 @@ func NewServerConfig(kex crypto.KeyExchange, signer crypto.Signer) (*ServerConfi } return &ServerConfig{ + ID: id, + obit: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}, kex: kex, signer: signer, - ID: id, stkSource: stkSource, }, nil } @@ -48,7 +50,7 @@ func (s *ServerConfig) Get() []byte { TagKEXS: []byte("C255"), TagAEAD: []byte("AESG"), TagPUBS: append([]byte{0x20, 0x00, 0x00}, s.kex.PublicKey()...), - TagOBIT: {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}, + TagOBIT: s.obit, TagEXPY: {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }) return serverConfig.Bytes()