move server proof verification to crypto package

This commit is contained in:
Marten Seemann
2016-11-18 10:22:25 +07:00
parent 0535491f30
commit bb1af0db1e
6 changed files with 175 additions and 165 deletions

View File

@@ -2,17 +2,10 @@ package handshake
import (
"bytes"
gocrypto "crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/asn1"
"encoding/binary"
"errors"
"io"
"math/big"
"time"
"github.com/lucas-clemente/quic-go/crypto"
@@ -21,10 +14,6 @@ import (
"github.com/lucas-clemente/quic-go/utils"
)
type ecdsaSignature struct {
R, S *big.Int
}
type cryptoSetupClient struct {
connID protocol.ConnectionID
version protocol.VersionNumber
@@ -166,35 +155,12 @@ func (h *cryptoSetupClient) handleREJMessage(cryptoData map[Tag][]byte) error {
}
func (h *cryptoSetupClient) verifyServerConfigSignature() error {
leafCert := h.certManager.GetLeafCert()
cert, err := x509.ParseCertificate(leafCert)
validProof, err := h.certManager.VerifyServerProof(h.proof, h.chloForSignature, h.serverConfig.Get())
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 {
signature := &ecdsaSignature{}
rest, err := asn1.Unmarshal(h.proof, signature)
if err != nil || len(rest) != 0 {
return qerr.ProofInvalid
}
if !ecdsa.Verify(cert.PublicKey.(*ecdsa.PublicKey), hash.Sum(nil), signature.R, signature.S) {
return qerr.ProofInvalid
}
if !validProof {
return qerr.ProofInvalid
}
// TODO: verify certificate chain

View File

@@ -3,15 +3,12 @@ package handshake
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/binary"
"encoding/pem"
"errors"
"fmt"
"math/big"
"os"
"time"
@@ -46,6 +43,9 @@ func pemBlockForCert(certDER []byte) *pem.Block {
type mockCertManager struct {
setDataCalledWith []byte
leafCert []byte
verifyServerProofError error
verifyServerProofValue bool
}
func (m *mockCertManager) SetData(data []byte) error {
@@ -57,6 +57,10 @@ func (m *mockCertManager) GetLeafCert() []byte {
return m.leafCert
}
func (m *mockCertManager) VerifyServerProof(proof, chlo, serverConfigData []byte) (bool, error) {
return m.verifyServerProofValue, m.verifyServerProofError
}
var _ = Describe("Crypto setup", func() {
var cs *cryptoSetupClient
var certManager *mockCertManager
@@ -133,20 +137,9 @@ var _ = Describe("Crypto setup", func() {
})
Context("Certificates", func() {
certTemplate := x509.Certificate{SerialNumber: big.NewInt(1)}
getTlsConfig := func(key interface{}, certDER []byte) *tls.Config {
// export certificate and key in PEM format
b := pemBlockForCert(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())
return &tls.Config{Certificates: []tls.Certificate{tlsCert}}
}
BeforeEach(func() {
cs.serverConfig = &serverConfigClient{}
})
It("passes the certificates to the CertManager", func() {
tagMap[TagCERT] = []byte("cert")
@@ -155,104 +148,25 @@ var _ = Describe("Crypto setup", func() {
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", func() {
certManager.verifyServerProofValue = true
certManager.verifyServerProofError = nil
err := cs.verifyServerConfigSignature()
Expect(err).ToNot(HaveOccurred())
})
It("errors if it can't read the leaf certificate", func() {
certManager.leafCert = []byte("invalid leaf cert")
It("errors when it can't read the certificate", func() {
certManager.verifyServerProofValue = true
certManager.verifyServerProofError = errors.New("test error")
err := cs.verifyServerConfigSignature()
Expect(err).To(MatchError(qerr.Error(qerr.InvalidCryptoMessageParameter, "Certificate data invalid")))
})
Context("RSA keys", func() {
It("verifies the signature of the server config, for an RSA key", func() {
// generate a RSA key pair and a certificate
key, err := rsa.GenerateKey(rand.Reader, 1024)
Expect(err).ToNot(HaveOccurred())
certDER, err := x509.CreateCertificate(rand.Reader, &certTemplate, &certTemplate, &key.PublicKey, key)
Expect(err).ToNot(HaveOccurred())
leafCert, err := x509.ParseCertificate(certDER)
Expect(err).ToNot(HaveOccurred())
tlsConfig := getTlsConfig(key, certDER)
cs.chloForSignature = []byte("CHLO for signature")
serverConfigData := []byte("Server Config Data")
cs.serverConfig = &serverConfigClient{raw: serverConfigData}
certManager.leafCert = leafCert.Raw
cc, err := crypto.NewCertChain(tlsConfig)
Expect(err).ToNot(HaveOccurred())
signature, err := cc.SignServerProof("", cs.chloForSignature, serverConfigData)
Expect(err).ToNot(HaveOccurred())
cs.proof = signature
err = cs.verifyServerConfigSignature()
Expect(err).ToNot(HaveOccurred())
})
It("rejects invalid signatures of the server config, for an RSA key", func() {
key, err := rsa.GenerateKey(rand.Reader, 1024)
Expect(err).ToNot(HaveOccurred())
certDER, err := x509.CreateCertificate(rand.Reader, &certTemplate, &certTemplate, &key.PublicKey, key)
Expect(err).ToNot(HaveOccurred())
leafCert, err := x509.ParseCertificate(certDER)
Expect(err).ToNot(HaveOccurred())
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("ECDSA keys", func() {
It("verifies the signature of the server config, for ECDSA keys", func() {
// generate a ECDSA key pair and a certificate
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
Expect(err).ToNot(HaveOccurred())
certDER, err := x509.CreateCertificate(rand.Reader, &certTemplate, &certTemplate, &key.PublicKey, key)
Expect(err).ToNot(HaveOccurred())
leafCert, err := x509.ParseCertificate(certDER)
Expect(err).ToNot(HaveOccurred())
tlsConfig := getTlsConfig(key, certDER)
cs.chloForSignature = []byte("CHLO for signature")
serverConfigData := []byte("Server Config Data")
cs.serverConfig = &serverConfigClient{raw: serverConfigData}
certManager.leafCert = leafCert.Raw
cc, err := crypto.NewCertChain(tlsConfig)
Expect(err).ToNot(HaveOccurred())
signature, err := cc.SignServerProof("", cs.chloForSignature, serverConfigData)
Expect(err).ToNot(HaveOccurred())
cs.proof = signature
err = cs.verifyServerConfigSignature()
Expect(err).ToNot(HaveOccurred())
})
It("rejects invalid signatures of the server config, for an RSA key", func() {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
Expect(err).ToNot(HaveOccurred())
certDER, err := x509.CreateCertificate(rand.Reader, &certTemplate, &certTemplate, &key.PublicKey, key)
Expect(err).ToNot(HaveOccurred())
leafCert, err := x509.ParseCertificate(certDER)
Expect(err).ToNot(HaveOccurred())
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))
})
It("rejects wrong signatures", func() {
certManager.verifyServerProofValue = false
certManager.verifyServerProofError = nil
err := cs.verifyServerConfigSignature()
Expect(err).To(MatchError(qerr.ProofInvalid))
})
})