forked from quic-go/quic-go
@@ -1,9 +1,11 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"hash/fnv"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
)
|
||||
@@ -19,7 +21,8 @@ type CertManager interface {
|
||||
}
|
||||
|
||||
type certManager struct {
|
||||
chain []*x509.Certificate
|
||||
chain []*x509.Certificate
|
||||
config *tls.Config
|
||||
}
|
||||
|
||||
var _ CertManager = &certManager{}
|
||||
@@ -27,8 +30,8 @@ var _ CertManager = &certManager{}
|
||||
var errNoCertificateChain = errors.New("CertManager BUG: No certicifate chain loaded")
|
||||
|
||||
// NewCertManager creates a new CertManager
|
||||
func NewCertManager() CertManager {
|
||||
return &certManager{}
|
||||
func NewCertManager(tlsConfig *tls.Config) CertManager {
|
||||
return &certManager{config: tlsConfig}
|
||||
}
|
||||
|
||||
// SetData takes the byte-slice sent in the SHLO and decompresses it into the certificate chain
|
||||
@@ -95,8 +98,24 @@ func (c *certManager) Verify(hostname string) error {
|
||||
return errNoCertificateChain
|
||||
}
|
||||
|
||||
if c.config != nil && c.config.InsecureSkipVerify {
|
||||
return nil
|
||||
}
|
||||
|
||||
leafCert := c.chain[0]
|
||||
opts := x509.VerifyOptions{DNSName: hostname}
|
||||
|
||||
var opts x509.VerifyOptions
|
||||
if c.config != nil {
|
||||
opts.Roots = c.config.RootCAs
|
||||
opts.DNSName = c.config.ServerName
|
||||
if c.config.Time == nil {
|
||||
opts.CurrentTime = time.Now()
|
||||
} else {
|
||||
opts.CurrentTime = c.config.Time()
|
||||
}
|
||||
} else {
|
||||
opts.DNSName = hostname
|
||||
}
|
||||
|
||||
// the first certificate is the leaf certificate, all others are intermediates
|
||||
if len(c.chain) > 1 {
|
||||
|
||||
@@ -25,7 +25,7 @@ var _ = Describe("Cert Manager", func() {
|
||||
|
||||
BeforeEach(func() {
|
||||
var err error
|
||||
cm = NewCertManager().(*certManager)
|
||||
cm = NewCertManager(nil).(*certManager)
|
||||
key1, err = rsa.GenerateKey(rand.Reader, 768)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
key2, err = rsa.GenerateKey(rand.Reader, 768)
|
||||
@@ -37,6 +37,12 @@ var _ = Describe("Cert Manager", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("saves a client TLS config", func() {
|
||||
tlsConf := &tls.Config{ServerName: "quic.clemente.io"}
|
||||
cm = NewCertManager(tlsConf).(*certManager)
|
||||
Expect(cm.config.ServerName).To(Equal("quic.clemente.io"))
|
||||
})
|
||||
|
||||
It("errors when given invalid data", func() {
|
||||
err := cm.SetData([]byte("foobar"))
|
||||
Expect(err).To(MatchError(qerr.Error(qerr.InvalidCryptoMessageParameter, "Certificate data invalid")))
|
||||
@@ -239,5 +245,111 @@ var _ = Describe("Cert Manager", func() {
|
||||
_, ok := err.(x509.UnknownAuthorityError)
|
||||
Expect(ok).To(BeTrue())
|
||||
})
|
||||
|
||||
It("doesn't do any certificate verification if InsecureSkipVerify is set", func() {
|
||||
if runtime.GOOS == "windows" {
|
||||
// certificate validation works different on windows, see https://golang.org/src/crypto/x509/verify.go line 238
|
||||
Skip("windows")
|
||||
}
|
||||
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
}
|
||||
|
||||
leafCert := getCertificate(template)
|
||||
cm.config = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
cm.chain = []*x509.Certificate{leafCert}
|
||||
err := cm.Verify("quic.clemente.io")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("uses a different hostname from a client TLS config", func() {
|
||||
if runtime.GOOS == "windows" {
|
||||
// certificate validation works different on windows, see https://golang.org/src/crypto/x509/verify.go line 238
|
||||
Skip("windows")
|
||||
}
|
||||
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
NotBefore: time.Now().Add(-time.Hour),
|
||||
NotAfter: time.Now().Add(time.Hour),
|
||||
Subject: pkix.Name{CommonName: "google.com"},
|
||||
}
|
||||
|
||||
leafCert := getCertificate(template)
|
||||
cm.chain = []*x509.Certificate{leafCert}
|
||||
cm.config = &tls.Config{
|
||||
ServerName: "google.com",
|
||||
}
|
||||
err := cm.Verify("quic.clemente.io")
|
||||
_, ok := err.(x509.UnknownAuthorityError)
|
||||
Expect(ok).To(BeTrue())
|
||||
})
|
||||
|
||||
It("rejects certificates with a different hostname than specified in the client TLS config", func() {
|
||||
if runtime.GOOS == "windows" {
|
||||
// certificate validation works different on windows, see https://golang.org/src/crypto/x509/verify.go line 238
|
||||
Skip("windows")
|
||||
}
|
||||
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
NotBefore: time.Now().Add(-time.Hour),
|
||||
NotAfter: time.Now().Add(time.Hour),
|
||||
Subject: pkix.Name{CommonName: "quic.clemente.io"},
|
||||
}
|
||||
|
||||
leafCert := getCertificate(template)
|
||||
cm.chain = []*x509.Certificate{leafCert}
|
||||
cm.config = &tls.Config{
|
||||
ServerName: "google.com",
|
||||
}
|
||||
err := cm.Verify("quic.clemente.io")
|
||||
_, ok := err.(x509.HostnameError)
|
||||
Expect(ok).To(BeTrue())
|
||||
})
|
||||
|
||||
It("uses the time specified in a client TLS config", func() {
|
||||
if runtime.GOOS == "windows" {
|
||||
// certificate validation works different on windows, see https://golang.org/src/crypto/x509/verify.go line 238
|
||||
Skip("windows")
|
||||
}
|
||||
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
NotBefore: time.Now().Add(-25 * time.Hour),
|
||||
NotAfter: time.Now().Add(-23 * time.Hour),
|
||||
}
|
||||
leafCert := getCertificate(template)
|
||||
cm.chain = []*x509.Certificate{leafCert}
|
||||
cm.config = &tls.Config{
|
||||
Time: func() time.Time { return time.Now().Add(-24 * time.Hour) },
|
||||
}
|
||||
err := cm.Verify("quic.clemente.io")
|
||||
_, ok := err.(x509.UnknownAuthorityError)
|
||||
Expect(ok).To(BeTrue())
|
||||
})
|
||||
|
||||
It("rejects certificates that are expired at the time specified in a client TLS config", func() {
|
||||
if runtime.GOOS == "windows" {
|
||||
// certificate validation works different on windows, see https://golang.org/src/crypto/x509/verify.go line 238
|
||||
Skip("windows")
|
||||
}
|
||||
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
NotBefore: time.Now().Add(-time.Hour),
|
||||
NotAfter: time.Now().Add(time.Hour),
|
||||
}
|
||||
leafCert := getCertificate(template)
|
||||
cm.chain = []*x509.Certificate{leafCert}
|
||||
cm.config = &tls.Config{
|
||||
Time: func() time.Time { return time.Now().Add(-24 * time.Hour) },
|
||||
}
|
||||
err := cm.Verify("quic.clemente.io")
|
||||
Expect(err.(x509.CertificateInvalidError).Reason).To(Equal(x509.Expired))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -73,7 +73,7 @@ func NewCryptoSetupClient(
|
||||
connID: connID,
|
||||
version: version,
|
||||
cryptoStream: cryptoStream,
|
||||
certManager: crypto.NewCertManager(),
|
||||
certManager: crypto.NewCertManager(nil),
|
||||
connectionParameters: connectionParameters,
|
||||
keyDerivation: crypto.DeriveKeysAESGCM,
|
||||
aeadChanged: aeadChanged,
|
||||
|
||||
Reference in New Issue
Block a user