forked from quic-go/quic-go
send leaf certificate hash (XLCT) in client hello
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user