From 674287a8f4491ca6429232fb63374c8dafd68f8e Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Tue, 8 Nov 2016 17:49:28 +0700 Subject: [PATCH] add a method to generate a client nonce (NONC) --- handshake/crypto_setup_client.go | 25 +++++++++++++++++++ handshake/crypto_setup_client_test.go | 35 +++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/handshake/crypto_setup_client.go b/handshake/crypto_setup_client.go index 128b7ec30..9a28a4d93 100644 --- a/handshake/crypto_setup_client.go +++ b/handshake/crypto_setup_client.go @@ -2,8 +2,11 @@ package handshake import ( "bytes" + "crypto/rand" "encoding/binary" + "errors" "io" + "time" "github.com/lucas-clemente/quic-go/crypto" "github.com/lucas-clemente/quic-go/protocol" @@ -23,6 +26,10 @@ type cryptoSetupClient struct { var _ crypto.AEAD = &cryptoSetupClient{} var _ CryptoSetup = &cryptoSetupClient{} +var ( + errNoObitForClientNonce = errors.New("No OBIT for client nonce available") +) + // NewCryptoSetupClient creates a new CryptoSetup instance for a client func NewCryptoSetupClient( connID protocol.ConnectionID, @@ -114,3 +121,21 @@ func (h *cryptoSetupClient) sendInchoateCHLO() error { } return nil } + +func (h *cryptoSetupClient) generateClientNonce() ([]byte, error) { + nonce := make([]byte, 32) + binary.BigEndian.PutUint32(nonce, uint32(time.Now().Unix())) + + if len(h.serverConfig.obit) != 8 { + return nil, errNoObitForClientNonce + } + + copy(nonce[4:12], h.serverConfig.obit) + + _, err := rand.Read(nonce[12:]) + if err != nil { + return nil, err + } + + return nonce, nil +} diff --git a/handshake/crypto_setup_client_test.go b/handshake/crypto_setup_client_test.go index 5b8e54b74..7cb4ee4b6 100644 --- a/handshake/crypto_setup_client_test.go +++ b/handshake/crypto_setup_client_test.go @@ -1,6 +1,9 @@ package handshake import ( + "encoding/binary" + "time" + "github.com/lucas-clemente/quic-go/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -10,8 +13,10 @@ var _ = Describe("Crypto setup", func() { var cs cryptoSetupClient BeforeEach(func() { + scfg := serverConfigClient{} cs = cryptoSetupClient{ cryptoStream: &mockStream{}, + serverConfig: &scfg, version: protocol.Version36, } }) @@ -30,4 +35,34 @@ var _ = Describe("Crypto setup", func() { Expect(cs.cryptoStream.(*mockStream).dataWritten.Len()).To(BeNumerically(">", protocol.ClientHelloMinimumSize)) }) }) + + Context("Client Nonce generation", func() { + BeforeEach(func() { + cs.serverConfig.obit = []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8} + }) + + It("generates a client nonce", func() { + now := time.Now() + nonce, err := cs.generateClientNonce() + Expect(nonce).To(HaveLen(32)) + Expect(err).ToNot(HaveOccurred()) + Expect(time.Unix(int64(binary.BigEndian.Uint32(nonce[0:4])), 0)).To(BeTemporally("~", now, 1*time.Second)) + Expect(nonce[4:12]).To(Equal(cs.serverConfig.obit)) + }) + + It("uses random values for the last 20 bytes", func() { + nonce1, err := cs.generateClientNonce() + Expect(err).ToNot(HaveOccurred()) + nonce2, err := cs.generateClientNonce() + Expect(err).ToNot(HaveOccurred()) + Expect(nonce1[4:12]).To(Equal(nonce2[4:12])) + Expect(nonce1[12:]).ToNot(Equal(nonce2[12:])) + }) + + It("errors if no OBIT value is available", func() { + cs.serverConfig.obit = []byte{} + _, err := cs.generateClientNonce() + Expect(err).To(MatchError(errNoObitForClientNonce)) + }) + }) })