validate server config signature, for RSA certificates

This commit is contained in:
Marten Seemann
2016-11-16 17:50:08 +07:00
parent 5aae3fbafe
commit 58b905e636
2 changed files with 149 additions and 5 deletions

View File

@@ -2,7 +2,11 @@ package handshake
import (
"bytes"
gocrypto "crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/binary"
"errors"
"io"
@@ -27,6 +31,7 @@ type cryptoSetupClient struct {
nonc []byte
proof []byte
diversificationNonce []byte
chloForSignature []byte
lastSentCHLO []byte
certManager crypto.CertManager
@@ -135,6 +140,7 @@ func (h *cryptoSetupClient) handleREJMessage(cryptoData map[Tag][]byte) error {
if proof, ok := cryptoData[TagPROF]; ok {
h.proof = proof
h.chloForSignature = h.lastSentCHLO
}
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
}

View File

@@ -2,7 +2,16 @@ package handshake
import (
"bytes"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/binary"
"encoding/pem"
"fmt"
"math/big"
"os"
"time"
"github.com/lucas-clemente/quic-go/crypto"
@@ -12,6 +21,23 @@ import (
. "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 {
setDataCalledWith []byte
leafCert []byte
@@ -81,6 +107,18 @@ var _ = Describe("Crypto setup", func() {
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() {
nonc := []byte("servernonce")
tagMap[TagSNO] = nonc
@@ -89,11 +127,79 @@ var _ = Describe("Crypto setup", func() {
Expect(cs.sno).To(Equal(nonc))
})
It("passes the certificates to the CertManager", func() {
tagMap[TagCERT] = []byte("cert")
err := cs.handleREJMessage(tagMap)
Expect(err).ToNot(HaveOccurred())
Expect(certManager.setDataCalledWith).To(Equal(tagMap[TagCERT]))
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() {
tagMap[TagCERT] = []byte("cert")
err := cs.handleREJMessage(tagMap)
Expect(err).ToNot(HaveOccurred())
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() {