From ac0eed61f25c2b6215ee64256a04bd6af891ac11 Mon Sep 17 00:00:00 2001 From: Lucas Clemente Date: Sun, 17 Apr 2016 00:21:54 +0200 Subject: [PATCH] introduce crypto.Signer interface and hide RSA implementation --- crypto/proof_rsa.go | 16 ++++++++-------- crypto/proof_rsa_test.go | 8 ++++---- crypto/signer.go | 8 ++++++++ handshake/crypto_setup.go | 4 ++-- handshake/server_config.go | 18 +++++++++--------- server.go | 10 +++++----- 6 files changed, 36 insertions(+), 28 deletions(-) create mode 100644 crypto/signer.go diff --git a/crypto/proof_rsa.go b/crypto/proof_rsa.go index 773bd1d3..ab7eea0f 100644 --- a/crypto/proof_rsa.go +++ b/crypto/proof_rsa.go @@ -14,14 +14,14 @@ import ( "github.com/lucas-clemente/quic-go/utils" ) -// KeyData stores a key and a certificate for the server proof -type KeyData struct { +// rsaSigner stores a key and a certificate for the server proof +type rsaSigner struct { key *rsa.PrivateKey cert *x509.Certificate } -// LoadKeyData loads the key and cert from files -func LoadKeyData(certFileName string, keyFileName string) (*KeyData, error) { +// NewRSASigner loads the key and cert from files +func NewRSASigner(certFileName string, keyFileName string) (Signer, error) { keyDER, err := ioutil.ReadFile(keyFileName) if err != nil { return nil, err @@ -39,11 +39,11 @@ func LoadKeyData(certFileName string, keyFileName string) (*KeyData, error) { return nil, err } - return &KeyData{key: key, cert: cert}, nil + return &rsaSigner{key: key, cert: cert}, nil } // SignServerProof signs CHLO and server config for use in the server proof -func (kd *KeyData) SignServerProof(chlo []byte, serverConfigData []byte) ([]byte, error) { +func (kd *rsaSigner) SignServerProof(chlo []byte, serverConfigData []byte) ([]byte, error) { hash := sha256.New() if len(chlo) > 0 { // Version >= 31 @@ -59,7 +59,7 @@ func (kd *KeyData) SignServerProof(chlo []byte, serverConfigData []byte) ([]byte } // GetCertCompressed gets the certificate in the format described by the QUIC crypto doc -func (kd *KeyData) GetCertCompressed() []byte { +func (kd *rsaSigner) GetCertCompressed() []byte { b := &bytes.Buffer{} b.WriteByte(1) // Entry type compressed b.WriteByte(0) // Entry type end_of_list @@ -81,6 +81,6 @@ func (kd *KeyData) GetCertCompressed() []byte { } // GetCertUncompressed gets the certificate in DER -func (kd *KeyData) GetCertUncompressed() []byte { +func (kd *rsaSigner) GetCertUncompressed() []byte { return kd.cert.Raw } diff --git a/crypto/proof_rsa_test.go b/crypto/proof_rsa_test.go index 19c66c1c..3a80e971 100644 --- a/crypto/proof_rsa_test.go +++ b/crypto/proof_rsa_test.go @@ -22,7 +22,7 @@ var _ = Describe("ProofRsa", func() { z.Write([]byte{0x04, 0x00, 0x00, 0x00}) z.Write(cert) z.Close() - kd := &KeyData{cert: &x509.Certificate{Raw: cert}} + kd := &rsaSigner{cert: &x509.Certificate{Raw: cert}} Expect(kd.GetCertCompressed()).To(Equal(append([]byte{ 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, @@ -31,14 +31,14 @@ var _ = Describe("ProofRsa", func() { It("gives valid signatures", func() { path := os.Getenv("GOPATH") + "/src/github.com/lucas-clemente/quic-go/example/" - keyData, err := LoadKeyData(path+"cert.der", path+"key.der") + kd, err := NewRSASigner(path+"cert.der", path+"key.der") Expect(err).ToNot(HaveOccurred()) - signature, err := keyData.SignServerProof([]byte{'C', 'H', 'L', 'O'}, []byte{'S', 'C', 'F', 'G'}) + signature, err := kd.SignServerProof([]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(keyData.cert.PublicKey.(*rsa.PublicKey), crypto.SHA256, data, signature, &rsa.PSSOptions{SaltLength: 32}) + err = rsa.VerifyPSS(kd.(*rsaSigner).cert.PublicKey.(*rsa.PublicKey), crypto.SHA256, data, signature, &rsa.PSSOptions{SaltLength: 32}) Expect(err).ToNot(HaveOccurred()) }) }) diff --git a/crypto/signer.go b/crypto/signer.go new file mode 100644 index 00000000..7eb8304c --- /dev/null +++ b/crypto/signer.go @@ -0,0 +1,8 @@ +package crypto + +// A Signer holds a certificate and a private key +type Signer interface { + SignServerProof(chlo []byte, serverConfigData []byte) ([]byte, error) + GetCertCompressed() []byte + GetCertUncompressed() []byte +} diff --git a/handshake/crypto_setup.go b/handshake/crypto_setup.go index 6546eeda..db1295ea 100644 --- a/handshake/crypto_setup.go +++ b/handshake/crypto_setup.go @@ -93,12 +93,12 @@ func (h *CryptoSetup) HandleCryptoMessage(data []byte) ([]byte, error) { nonce.Write(cryptoData[TagNONC]) nonce.Write(h.nonce) - h.secureAEAD, err = crypto.DeriveKeysChacha20(false, sharedSecret, nonce.Bytes(), h.connID, data, h.scfg.Get(), h.scfg.kd.GetCertUncompressed()) + h.secureAEAD, err = crypto.DeriveKeysChacha20(false, sharedSecret, nonce.Bytes(), h.connID, data, h.scfg.Get(), h.scfg.signer.GetCertUncompressed()) if err != nil { return nil, err } // TODO: Use new curve - h.forwardSecureAEAD, err = crypto.DeriveKeysChacha20(true, sharedSecret, nonce.Bytes(), h.connID, data, h.scfg.Get(), h.scfg.kd.GetCertUncompressed()) + h.forwardSecureAEAD, err = crypto.DeriveKeysChacha20(true, sharedSecret, nonce.Bytes(), h.connID, data, h.scfg.Get(), h.scfg.signer.GetCertUncompressed()) if err != nil { return nil, err } diff --git a/handshake/server_config.go b/handshake/server_config.go index 8509b5d0..15db4857 100644 --- a/handshake/server_config.go +++ b/handshake/server_config.go @@ -9,22 +9,22 @@ import ( // ServerConfig is a server config type ServerConfig struct { - kex crypto.KeyExchange - kd *crypto.KeyData - ID []byte + kex crypto.KeyExchange + signer crypto.Signer + ID []byte } // NewServerConfig creates a new server config -func NewServerConfig(kex crypto.KeyExchange, kd *crypto.KeyData) *ServerConfig { +func NewServerConfig(kex crypto.KeyExchange, signer crypto.Signer) *ServerConfig { id := make([]byte, 16) _, err := rand.Reader.Read(id) if err != nil { panic(err) } return &ServerConfig{ - kex: kex, - kd: kd, - ID: id, + kex: kex, + signer: signer, + ID: id, } } @@ -45,10 +45,10 @@ func (s *ServerConfig) Get() []byte { // Sign the server config and CHLO with the server's keyData func (s *ServerConfig) Sign(chlo []byte) ([]byte, error) { - return s.kd.SignServerProof(chlo, s.Get()) + return s.signer.SignServerProof(chlo, s.Get()) } // GetCertCompressed returns the certificate data func (s *ServerConfig) GetCertCompressed() []byte { - return s.kd.GetCertCompressed() + return s.signer.GetCertCompressed() } diff --git a/server.go b/server.go index 039a4fc3..c387cbf4 100644 --- a/server.go +++ b/server.go @@ -13,8 +13,8 @@ import ( // A Server of QUIC type Server struct { - keyData *crypto.KeyData - scfg *handshake.ServerConfig + signer crypto.Signer + scfg *handshake.ServerConfig sessions map[protocol.ConnectionID]*Session @@ -24,15 +24,15 @@ type Server struct { // NewServer makes a new server func NewServer(certPath, keyPath string, cb StreamCallback) (*Server, error) { path := os.Getenv("GOPATH") + "/src/github.com/lucas-clemente/quic-go/example/" - keyData, err := crypto.LoadKeyData(path+"cert.der", path+"key.der") + signer, err := crypto.NewRSASigner(path+"cert.der", path+"key.der") if err != nil { return nil, err } - scfg := handshake.NewServerConfig(crypto.NewCurve25519KEX(), keyData) + scfg := handshake.NewServerConfig(crypto.NewCurve25519KEX(), signer) return &Server{ - keyData: keyData, + signer: signer, scfg: scfg, streamCallback: cb, sessions: map[protocol.ConnectionID]*Session{},