send leaf certificate hash (XLCT) in client hello

This commit is contained in:
Marten Seemann
2016-12-03 11:53:49 +07:00
parent 11cb69d2ce
commit 86da7dce81
4 changed files with 73 additions and 14 deletions

View File

@@ -3,6 +3,7 @@ package crypto
import (
"crypto/x509"
"errors"
"hash/fnv"
"github.com/lucas-clemente/quic-go/qerr"
)
@@ -11,6 +12,7 @@ import (
type CertManager interface {
SetData([]byte) error
GetLeafCert() []byte
GetLeafCertHash() (uint64, error)
VerifyServerProof(proof, chlo, serverConfigData []byte) bool
Verify(hostname string) error
}
@@ -49,7 +51,7 @@ func (c *certManager) SetData(data []byte) error {
}
// GetLeafCert returns the leaf certificate of the certificate chain
// it errors if the certificate chain has not yet been set
// it returns nil if the certificate chain has not yet been set
func (c *certManager) GetLeafCert() []byte {
if len(c.chain) == 0 {
return nil
@@ -57,6 +59,21 @@ func (c *certManager) GetLeafCert() []byte {
return c.chain[0].Raw
}
// GetLeafCertHash calculates the FNV1a_64 hash of the leaf certificate
func (c *certManager) GetLeafCertHash() (uint64, error) {
leafCert := c.GetLeafCert()
if leafCert == nil {
return 0, errNoCertificateChain
}
h := fnv.New64a()
_, err := h.Write(leafCert)
if err != nil {
return 0, err
}
return h.Sum64(), nil
}
// VerifyServerProof verifies the signature of the server config
// it should only be called after the certificate chain has been set, otherwise it returns false
func (c *certManager) VerifyServerProof(proof, chlo, serverConfigData []byte) bool {

View File

@@ -85,6 +85,24 @@ var _ = Describe("Cert Manager", func() {
})
})
Context("getting the leaf cert hash", func() {
It("calculates the FVN1a 64 hash", func() {
cm.chain = make([]*x509.Certificate, 1)
cm.chain[0] = &x509.Certificate{
Raw: []byte("test fnv hash"),
}
hash, err := cm.GetLeafCertHash()
Expect(err).ToNot(HaveOccurred())
// hash calculated on http://www.nitrxgen.net/hashgen/
Expect(hash).To(Equal(uint64(0x4770f6141fa0f5ad)))
})
It("errors if the certificate chain is not loaded", func() {
_, err := cm.GetLeafCertHash()
Expect(err).To(MatchError(errNoCertificateChain))
})
})
Context("verifying the server config signature", func() {
It("returns false when the chain hasn't been set yet", func() {
valid := cm.VerifyServerProof([]byte("proof"), []byte("chlo"), []byte("scfg"))

View File

@@ -274,12 +274,15 @@ func (h *cryptoSetupClient) sendCHLO() error {
b := &bytes.Buffer{}
tags := h.getTags()
tags, err := h.getTags()
if err != nil {
return err
}
h.addPadding(tags)
WriteHandshakeMessage(b, TagCHLO, tags)
_, err := h.cryptoStream.Write(b.Bytes())
_, err = h.cryptoStream.Write(b.Bytes())
if err != nil {
return err
}
@@ -289,7 +292,7 @@ func (h *cryptoSetupClient) sendCHLO() error {
return nil
}
func (h *cryptoSetupClient) getTags() map[Tag][]byte {
func (h *cryptoSetupClient) getTags() (map[Tag][]byte, error) {
tags := make(map[Tag][]byte)
tags[TagSNI] = []byte(h.hostname)
tags[TagPDMD] = []byte("X509")
@@ -311,12 +314,17 @@ func (h *cryptoSetupClient) getTags() map[Tag][]byte {
leafCert := h.certManager.GetLeafCert()
if leafCert != nil {
certHash, _ := h.certManager.GetLeafCertHash()
xlct := make([]byte, 8, 8)
binary.LittleEndian.PutUint64(xlct, certHash)
tags[TagNONC] = h.nonc
tags[TagXLCT] = xlct
tags[TagPUBS] = h.serverConfig.kex.PublicKey() // TODO: check if 3 bytes need to be prepended
}
}
return tags
return tags, nil
}
// add a TagPAD to a tagMap, such that the total size will be bigger than the ClientHelloMinimumSize

View File

@@ -45,6 +45,8 @@ type mockCertManager struct {
setDataError error
leafCert []byte
leafCertHash uint64
leafCertHashError error
verifyServerProofResult bool
verifyServerProofCalled bool
@@ -62,6 +64,10 @@ func (m *mockCertManager) GetLeafCert() []byte {
return m.leafCert
}
func (m *mockCertManager) GetLeafCertHash() (uint64, error) {
return m.leafCertHash, m.leafCertHashError
}
func (m *mockCertManager) VerifyServerProof(proof, chlo, serverConfigData []byte) bool {
m.verifyServerProofCalled = true
return m.verifyServerProofResult
@@ -355,7 +361,8 @@ var _ = Describe("Crypto setup", func() {
It("has the right values for an inchoate CHLO", func() {
cs.hostname = "sni-hostname"
tags := cs.getTags()
tags, err := cs.getTags()
Expect(err).ToNot(HaveOccurred())
Expect(string(tags[TagSNI])).To(Equal(cs.hostname))
Expect(tags[TagPDMD]).To(Equal([]byte("X509")))
Expect(tags[TagVER]).To(Equal([]byte("Q036")))
@@ -364,44 +371,53 @@ var _ = Describe("Crypto setup", func() {
It("includes the server config id, if available", func() {
id := []byte("foobar")
cs.serverConfig = &serverConfigClient{ID: id}
tags := cs.getTags()
tags, err := cs.getTags()
Expect(err).ToNot(HaveOccurred())
Expect(tags[TagSCID]).To(Equal(id))
})
It("includes the source address token, if available", func() {
cs.stk = []byte("sourceaddresstoken")
tags := cs.getTags()
tags, err := cs.getTags()
Expect(err).ToNot(HaveOccurred())
Expect(tags[TagSTK]).To(Equal(cs.stk))
})
It("includes the server nonce, if available", func() {
cs.sno = []byte("foobar")
tags := cs.getTags()
tags, err := cs.getTags()
Expect(err).ToNot(HaveOccurred())
Expect(tags[TagSNO]).To(Equal(cs.sno))
})
It("doesn't include optional values, if not available", func() {
tags := cs.getTags()
tags, err := cs.getTags()
Expect(err).ToNot(HaveOccurred())
Expect(tags).ToNot(HaveKey(TagSCID))
Expect(tags).ToNot(HaveKey(TagSNO))
Expect(tags).ToNot(HaveKey(TagSTK))
})
It("doesn't change any values after reading the certificate, if the server config is missing", func() {
tags := cs.getTags()
tags, err := cs.getTags()
Expect(err).ToNot(HaveOccurred())
certManager.leafCert = []byte("leafcert")
Expect(cs.getTags()).To(Equal(tags))
})
It("sends a client nonce and a public value after reading the certificate and the server config", func() {
It("sends a client nonce, a public value and the cert hash after reading the certificate and the server config", func() {
certManager.leafCert = []byte("leafcert")
cs.nonc = []byte("client-nonce")
kex, err := crypto.NewCurve25519KEX()
Expect(err).ToNot(HaveOccurred())
cs.serverConfig = &serverConfigClient{kex: kex}
tags := cs.getTags()
xlct := []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}
certManager.leafCertHash = binary.LittleEndian.Uint64(xlct)
tags, err := cs.getTags()
Expect(err).ToNot(HaveOccurred())
Expect(tags[TagNONC]).To(Equal(cs.nonc))
Expect(tags[TagPUBS]).To(Equal(kex.PublicKey()))
Expect(tags[TagXLCT]).To(Equal(xlct))
})
It("doesn't send more than MaxClientHellos CHLOs", func() {