forked from quic-go/quic-go
validate server config signature, for RSA certificates
This commit is contained in:
@@ -2,7 +2,11 @@ package handshake
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
gocrypto "crypto"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/x509"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
@@ -27,6 +31,7 @@ type cryptoSetupClient struct {
|
|||||||
nonc []byte
|
nonc []byte
|
||||||
proof []byte
|
proof []byte
|
||||||
diversificationNonce []byte
|
diversificationNonce []byte
|
||||||
|
chloForSignature []byte
|
||||||
lastSentCHLO []byte
|
lastSentCHLO []byte
|
||||||
certManager crypto.CertManager
|
certManager crypto.CertManager
|
||||||
|
|
||||||
@@ -135,6 +140,7 @@ func (h *cryptoSetupClient) handleREJMessage(cryptoData map[Tag][]byte) error {
|
|||||||
|
|
||||||
if proof, ok := cryptoData[TagPROF]; ok {
|
if proof, ok := cryptoData[TagPROF]; ok {
|
||||||
h.proof = proof
|
h.proof = proof
|
||||||
|
h.chloForSignature = h.lastSentCHLO
|
||||||
}
|
}
|
||||||
|
|
||||||
if crt, ok := cryptoData[TagCERT]; ok {
|
if crt, ok := cryptoData[TagCERT]; ok {
|
||||||
@@ -144,6 +150,38 @@ func (h *cryptoSetupClient) handleREJMessage(cryptoData map[Tag][]byte) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if h.serverConfig != nil && len(h.proof) != 0 && h.certManager.GetLeafCert() != nil {
|
||||||
|
return h.verifyServerConfigSignature()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *cryptoSetupClient) verifyServerConfigSignature() error {
|
||||||
|
leafCert := h.certManager.GetLeafCert()
|
||||||
|
cert, err := x509.ParseCertificate(leafCert)
|
||||||
|
if err != nil {
|
||||||
|
return qerr.Error(qerr.InvalidCryptoMessageParameter, "Certificate data invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := sha256.New()
|
||||||
|
hash.Write([]byte("QUIC CHLO and server config signature\x00"))
|
||||||
|
chloHash := sha256.Sum256(h.chloForSignature)
|
||||||
|
hash.Write([]byte{32, 0, 0, 0})
|
||||||
|
hash.Write(chloHash[:])
|
||||||
|
hash.Write(h.serverConfig.Get())
|
||||||
|
|
||||||
|
if cert.PublicKeyAlgorithm == x509.RSA {
|
||||||
|
opts := &rsa.PSSOptions{SaltLength: 32, Hash: gocrypto.SHA256}
|
||||||
|
err = rsa.VerifyPSS(cert.PublicKey.(*rsa.PublicKey), gocrypto.SHA256, hash.Sum(nil), h.proof, opts)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return qerr.ProofInvalid
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic("Not a RSA.")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,16 @@ package handshake
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/lucas-clemente/quic-go/crypto"
|
"github.com/lucas-clemente/quic-go/crypto"
|
||||||
@@ -12,6 +21,23 @@ import (
|
|||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// taken from https://golang.org/src/crypto/tls/generate_cert.go
|
||||||
|
func pemBlockForKey(priv interface{}) *pem.Block {
|
||||||
|
switch k := priv.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
b, err := x509.MarshalECPrivateKey(k)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type mockCertManager struct {
|
type mockCertManager struct {
|
||||||
setDataCalledWith []byte
|
setDataCalledWith []byte
|
||||||
leafCert []byte
|
leafCert []byte
|
||||||
@@ -81,6 +107,18 @@ var _ = Describe("Crypto setup", func() {
|
|||||||
Expect(cs.proof).To(Equal(proof))
|
Expect(cs.proof).To(Equal(proof))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("saves the last sent CHLO for signature validation, when receiving the proof", func() {
|
||||||
|
chlo := []byte("last sent CHLO")
|
||||||
|
cs.lastSentCHLO = chlo
|
||||||
|
err := cs.handleREJMessage(tagMap)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(cs.chloForSignature).To(BeEmpty())
|
||||||
|
tagMap[TagPROF] = []byte("signature")
|
||||||
|
err = cs.handleREJMessage(tagMap)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(cs.chloForSignature).To(Equal(chlo))
|
||||||
|
})
|
||||||
|
|
||||||
It("saves the server nonce", func() {
|
It("saves the server nonce", func() {
|
||||||
nonc := []byte("servernonce")
|
nonc := []byte("servernonce")
|
||||||
tagMap[TagSNO] = nonc
|
tagMap[TagSNO] = nonc
|
||||||
@@ -89,6 +127,34 @@ var _ = Describe("Crypto setup", func() {
|
|||||||
Expect(cs.sno).To(Equal(nonc))
|
Expect(cs.sno).To(Equal(nonc))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Context("Certificates", func() {
|
||||||
|
var leafCert *x509.Certificate
|
||||||
|
var tlsConfig *tls.Config
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
// generate a RSA key pair and a certificate
|
||||||
|
key, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
template := x509.Certificate{SerialNumber: big.NewInt(1)}
|
||||||
|
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
leafCert, err = x509.ParseCertificate(certDER)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
// export certificate and key in PEM format
|
||||||
|
b := &pem.Block{Type: "CERTIFICATE", Bytes: certDER}
|
||||||
|
certPEM := pem.EncodeToMemory(b)
|
||||||
|
b = pemBlockForKey(key)
|
||||||
|
keyPEM := pem.EncodeToMemory(b)
|
||||||
|
|
||||||
|
// create a tls.Config
|
||||||
|
tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
tlsConfig = &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{tlsCert},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
It("passes the certificates to the CertManager", func() {
|
It("passes the certificates to the CertManager", func() {
|
||||||
tagMap[TagCERT] = []byte("cert")
|
tagMap[TagCERT] = []byte("cert")
|
||||||
err := cs.handleREJMessage(tagMap)
|
err := cs.handleREJMessage(tagMap)
|
||||||
@@ -96,6 +162,46 @@ var _ = Describe("Crypto setup", func() {
|
|||||||
Expect(certManager.setDataCalledWith).To(Equal(tagMap[TagCERT]))
|
Expect(certManager.setDataCalledWith).To(Equal(tagMap[TagCERT]))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("verifies the signature, once it has read all required data", func() {
|
||||||
|
cs.serverConfig = &serverConfigClient{}
|
||||||
|
certManager.leafCert = []byte("leaf cert") // this certificate can't be parsed by x509
|
||||||
|
cs.proof = []byte("proof")
|
||||||
|
err := cs.handleREJMessage(tagMap)
|
||||||
|
Expect(err).To(MatchError(qerr.Error(qerr.InvalidCryptoMessageParameter, "Certificate data invalid")))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("verifies the signature of the server config", func() {
|
||||||
|
cs.chloForSignature = []byte("CHLO for signature")
|
||||||
|
serverConfigData := []byte("Server Config Data")
|
||||||
|
cs.serverConfig = &serverConfigClient{raw: serverConfigData}
|
||||||
|
certManager.leafCert = leafCert.Raw
|
||||||
|
|
||||||
|
ps, err := crypto.NewProofSource(tlsConfig)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
signature, err := ps.SignServerProof("", cs.chloForSignature, serverConfigData)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
cs.proof = signature
|
||||||
|
|
||||||
|
err = cs.verifyServerConfigSignature()
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("errors if it can't read the leaf certificate", func() {
|
||||||
|
certManager.leafCert = []byte("invalid leaf cert")
|
||||||
|
err := cs.verifyServerConfigSignature()
|
||||||
|
Expect(err).To(MatchError(qerr.Error(qerr.InvalidCryptoMessageParameter, "Certificate data invalid")))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("rejects invalid signatures of the server config", func() {
|
||||||
|
cs.serverConfig = &serverConfigClient{raw: []byte("Server Config Data")}
|
||||||
|
cs.proof = []byte("invalid signature")
|
||||||
|
certManager.leafCert = leafCert.Raw
|
||||||
|
|
||||||
|
err := cs.verifyServerConfigSignature()
|
||||||
|
Expect(err).To(MatchError(qerr.ProofInvalid))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
Context("Reading server configs", func() {
|
Context("Reading server configs", func() {
|
||||||
It("reads a server config", func() {
|
It("reads a server config", func() {
|
||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
|
|||||||
Reference in New Issue
Block a user