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

@@ -1,6 +1,7 @@
package crypto
import (
"crypto/x509"
"errors"
"github.com/lucas-clemente/quic-go/qerr"
@@ -10,6 +11,7 @@ import (
type CertManager interface {
SetData([]byte) error
GetLeafCert() []byte
VerifyServerProof(proof, chlo, serverConfigData []byte) (bool, error)
}
type certManager struct {
@@ -44,3 +46,17 @@ func (c *certManager) GetLeafCert() []byte {
}
return c.chain[0]
}
func (c *certManager) VerifyServerProof(proof, chlo, serverConfigData []byte) (bool, error) {
leafCert := c.GetLeafCert()
if leafCert == nil {
return false, errNoCertificateChain
}
cert, err := x509.ParseCertificate(leafCert)
if err != nil {
return false, err
}
return verifyServerProof(proof, cert, chlo, serverConfigData), nil
}

View File

@@ -43,4 +43,21 @@ var _ = Describe("Cert Manager", func() {
Expect(leafCert).To(BeNil())
})
})
Context("verifying the server signature", func() {
It("errors when the chain hasn't been set yet", func() {
valid, err := cm.VerifyServerProof([]byte("proof"), []byte("chlo"), []byte("scfg"))
Expect(err).To(MatchError(errNoCertificateChain))
Expect(valid).To(BeFalse())
})
It("errors when it can't parse the certificate", func() {
cert := []byte("invalid cert")
cm.chain = [][]byte{cert}
valid, err := cm.VerifyServerProof([]byte("proof"), []byte("chlo"), []byte("scfg"))
Expect(err).To(HaveOccurred())
Expect(err).ToNot(MatchError(errNoCertificateChain))
Expect(valid).To(BeFalse())
})
})
})

View File

@@ -2,10 +2,13 @@ package crypto
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/asn1"
"errors"
"math/big"
)
@@ -36,3 +39,28 @@ func signServerProof(cert *tls.Certificate, chlo []byte, serverConfigData []byte
return key.Sign(rand.Reader, hash.Sum(nil), opts)
}
// verifyServerProof verifies the server proof signature
func verifyServerProof(proof []byte, cert *x509.Certificate, chlo []byte, serverConfigData []byte) bool {
hash := sha256.New()
hash.Write([]byte("QUIC CHLO and server config signature\x00"))
chloHash := sha256.Sum256(chlo)
hash.Write([]byte{32, 0, 0, 0})
hash.Write(chloHash[:])
hash.Write(serverConfigData)
// RSA
if cert.PublicKeyAlgorithm == x509.RSA {
opts := &rsa.PSSOptions{SaltLength: 32, Hash: crypto.SHA256}
err := rsa.VerifyPSS(cert.PublicKey.(*rsa.PublicKey), crypto.SHA256, hash.Sum(nil), proof, opts)
return err == nil
}
// ECDSA
signature := &ecdsaSignature{}
rest, err := asn1.Unmarshal(proof, signature)
if err != nil || len(rest) != 0 {
return false
}
return ecdsa.Verify(cert.PublicKey.(*ecdsa.PublicKey), hash.Sum(nil), signature.R, signature.S)
}

View File

@@ -7,7 +7,9 @@ import (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/asn1"
"math/big"
"github.com/lucas-clemente/quic-go/testdata"
@@ -16,34 +18,69 @@ import (
)
var _ = Describe("Proof", func() {
It("gives valid signatures with the key in testdata", func() {
key := &testdata.GetTLSConfig().Certificates[0]
signature, err := signServerProof(key, []byte{'C', 'H', 'L', 'O'}, []byte{'S', 'C', 'F', 'G'})
Expect(err).ToNot(HaveOccurred())
// Generated with:
// ruby -e 'require "digest"; p Digest::SHA256.digest("QUIC CHLO and server config signature\x00" + "\x20\x00\x00\x00" + Digest::SHA256.digest("CHLO") + "SCFG")'
data := []byte("W\xA6\xFC\xDE\xC7\xD2>c\xE6\xB5\xF6\tq\x9E|<~1\xA33\x01\xCA=\x19\xBD\xC1\xE4\xB0\xBA\x9B\x16%")
err = rsa.VerifyPSS(key.PrivateKey.(*rsa.PrivateKey).Public().(*rsa.PublicKey), crypto.SHA256, data, signature, &rsa.PSSOptions{SaltLength: 32})
Expect(err).ToNot(HaveOccurred())
})
Context("when using RSA", func() {
It("gives valid signatures", func() {
key := &testdata.GetTLSConfig().Certificates[0]
signature, err := signServerProof(key, []byte{'C', 'H', 'L', 'O'}, []byte{'S', 'C', 'F', 'G'})
generateCert := func() (*rsa.PrivateKey, *x509.Certificate) {
key, err := rsa.GenerateKey(rand.Reader, 1024)
Expect(err).NotTo(HaveOccurred())
certTemplate := x509.Certificate{SerialNumber: big.NewInt(1)}
certDER, err := x509.CreateCertificate(rand.Reader, &certTemplate, &certTemplate, &key.PublicKey, key)
Expect(err).ToNot(HaveOccurred())
// Generated with:
// ruby -e 'require "digest"; p Digest::SHA256.digest("QUIC CHLO and server config signature\x00" + "\x20\x00\x00\x00" + Digest::SHA256.digest("CHLO") + "SCFG")'
data := []byte("W\xA6\xFC\xDE\xC7\xD2>c\xE6\xB5\xF6\tq\x9E|<~1\xA33\x01\xCA=\x19\xBD\xC1\xE4\xB0\xBA\x9B\x16%")
err = rsa.VerifyPSS(key.PrivateKey.(*rsa.PrivateKey).Public().(*rsa.PublicKey), crypto.SHA256, data, signature, &rsa.PSSOptions{SaltLength: 32})
cert, err := x509.ParseCertificate(certDER)
Expect(err).ToNot(HaveOccurred())
return key, cert
}
It("verifies a signature", func() {
key, cert := generateCert()
chlo := []byte("chlo")
scfg := []byte("scfg")
signature, err := signServerProof(&tls.Certificate{PrivateKey: key}, chlo, scfg)
Expect(err).ToNot(HaveOccurred())
Expect(verifyServerProof(signature, cert, chlo, scfg)).To(BeTrue())
})
It("rejects invalid signatures", func() {
key, cert := generateCert()
chlo := []byte("client hello")
scfg := []byte("sever config")
signature, err := signServerProof(&tls.Certificate{PrivateKey: key}, chlo, scfg)
Expect(err).ToNot(HaveOccurred())
Expect(verifyServerProof(append(signature, byte(0x99)), cert, chlo, scfg)).To(BeFalse())
Expect(verifyServerProof(signature, cert, chlo[:len(chlo)-2], scfg)).To(BeFalse())
Expect(verifyServerProof(signature, cert, chlo, scfg[:len(scfg)-2])).To(BeFalse())
})
})
Context("when using ECDSA", func() {
var (
key crypto.Signer
cert *tls.Certificate
)
BeforeEach(func() {
var err error
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
generateCert := func() (*ecdsa.PrivateKey, *x509.Certificate) {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
Expect(err).NotTo(HaveOccurred())
cert = &tls.Certificate{PrivateKey: key}
})
certTemplate := x509.Certificate{SerialNumber: big.NewInt(1)}
certDER, err := x509.CreateCertificate(rand.Reader, &certTemplate, &certTemplate, &key.PublicKey, key)
Expect(err).ToNot(HaveOccurred())
cert, err := x509.ParseCertificate(certDER)
Expect(err).ToNot(HaveOccurred())
return key, cert
}
It("gives valid signatures", func() {
signature, err := signServerProof(cert, []byte{'C', 'H', 'L', 'O'}, []byte{'S', 'C', 'F', 'G'})
key, _ := generateCert()
signature, err := signServerProof(&tls.Certificate{PrivateKey: key}, []byte{'C', 'H', 'L', 'O'}, []byte{'S', 'C', 'F', 'G'})
Expect(err).ToNot(HaveOccurred())
// Generated with:
// ruby -e 'require "digest"; p Digest::SHA256.digest("QUIC CHLO and server config signature\x00" + "\x20\x00\x00\x00" + Digest::SHA256.digest("CHLO") + "SCFG")'
@@ -54,5 +91,37 @@ var _ = Describe("Proof", func() {
b := ecdsa.Verify(key.Public().(*ecdsa.PublicKey), data, s.R, s.S)
Expect(b).To(BeTrue())
})
It("verifies a signature", func() {
key, cert := generateCert()
chlo := []byte("chlo")
scfg := []byte("server config")
signature, err := signServerProof(&tls.Certificate{PrivateKey: key}, chlo, scfg)
Expect(err).ToNot(HaveOccurred())
Expect(verifyServerProof(signature, cert, chlo, scfg)).To(BeTrue())
})
It("rejects invalid signatures", func() {
key, cert := generateCert()
chlo := []byte("client hello")
scfg := []byte("server config")
signature, err := signServerProof(&tls.Certificate{PrivateKey: key}, chlo, scfg)
Expect(err).ToNot(HaveOccurred())
Expect(verifyServerProof(append(signature, byte(0x99)), cert, chlo, scfg)).To(BeFalse())
Expect(verifyServerProof(signature, cert, chlo[:len(chlo)-2], scfg)).To(BeFalse())
Expect(verifyServerProof(signature, cert, chlo, scfg[:len(scfg)-2])).To(BeFalse())
})
It("rejects signatures generated with a different certificate", func() {
key1, cert1 := generateCert()
key2, cert2 := generateCert()
Expect(key1.PublicKey).ToNot(Equal(key2))
Expect(cert1.Equal(cert2)).To(BeFalse())
chlo := []byte("chlo")
scfg := []byte("sfcg")
signature, err := signServerProof(&tls.Certificate{PrivateKey: key1}, chlo, scfg)
Expect(err).ToNot(HaveOccurred())
Expect(verifyServerProof(signature, cert2, chlo, scfg)).To(BeFalse())
})
})
})