]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/tls: add ECDHE support
authorAdam Langley <agl@golang.org>
Thu, 16 Dec 2010 22:10:50 +0000 (17:10 -0500)
committerAdam Langley <agl@golang.org>
Thu, 16 Dec 2010 22:10:50 +0000 (17:10 -0500)
(ECDHE is "Elliptic Curve Diffie Hellman Ephemeral")

R=rsc
CC=golang-dev
https://golang.org/cl/3668042

src/pkg/crypto/rsa/pkcs1v15.go
src/pkg/crypto/tls/Makefile
src/pkg/crypto/tls/cipher_suites.go
src/pkg/crypto/tls/common.go
src/pkg/crypto/tls/conn.go
src/pkg/crypto/tls/handshake_client.go
src/pkg/crypto/tls/handshake_client_test.go [new file with mode: 0644]
src/pkg/crypto/tls/handshake_messages.go
src/pkg/crypto/tls/handshake_messages_test.go
src/pkg/crypto/tls/handshake_server.go
src/pkg/crypto/tls/key_agreement.go [new file with mode: 0644]

index f918d6352ef6d3548943d835edadb51cfd2744a9..7140462509cd8f61b3fd067f517375d143bb1556 100644 (file)
@@ -130,6 +130,9 @@ func nonZeroRandomBytes(s []byte, rand io.Reader) (err os.Error) {
                        if err != nil {
                                return
                        }
+                       // In tests, the PRNG may return all zeros so we do
+                       // this to break the loop.
+                       s[i] ^= 0x42
                }
        }
 
index 1995a8a827dda97210365dc703ae5c38d86411ed..f8ec1511aa17b442a2bc78ef24d3e7400aa15b38 100644 (file)
@@ -14,6 +14,7 @@ GOFILES=\
        handshake_client.go\
        handshake_messages.go\
        handshake_server.go\
+       key_agreement.go\
        prf.go\
        tls.go\
 
index 87a9f836bfcc8c35c978f44c800e133296541cb6..bc7b0d32f95c980bbf6af1d2540060e27ba769e5 100644 (file)
@@ -9,9 +9,30 @@ import (
        "crypto/cipher"
        "crypto/hmac"
        "crypto/rc4"
+       "crypto/x509"
        "hash"
+       "os"
 )
 
+// a keyAgreement implements the client and server side of a TLS key agreement
+// protocol by generating and processing key exchange messages.
+type keyAgreement interface {
+       // On the server side, the first two methods are called in order.
+
+       // In the case that the key agreement protocol doesn't use a
+       // ServerKeyExchange message, generateServerKeyExchange can return nil,
+       // nil.
+       generateServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, os.Error)
+       processClientKeyExchange(*Config, *clientKeyExchangeMsg) ([]byte, os.Error)
+
+       // On the client side, the next two methods are called in order.
+
+       // This method may not be called if the server doesn't send a
+       // ServerKeyExchange message.
+       processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) os.Error
+       generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, os.Error)
+}
+
 // A cipherSuite is a specific combination of key agreement, cipher and MAC
 // function. All cipher suites currently assume RSA key agreement.
 type cipherSuite struct {
@@ -19,13 +40,20 @@ type cipherSuite struct {
        keyLen int
        macLen int
        ivLen  int
-       cipher func(key, iv []byte, isRead bool) interface{}
-       mac    func(macKey []byte) hash.Hash
+       ka     func() keyAgreement
+       // If elliptic is set, a server will only consider this ciphersuite if
+       // the ClientHello indicated that the client supports an elliptic curve
+       // and point format that we can handle.
+       elliptic bool
+       cipher   func(key, iv []byte, isRead bool) interface{}
+       mac      func(macKey []byte) hash.Hash
 }
 
 var cipherSuites = map[uint16]*cipherSuite{
-       TLS_RSA_WITH_RC4_128_SHA:     &cipherSuite{16, 20, 0, cipherRC4, hmacSHA1},
-       TLS_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, cipherAES, hmacSHA1},
+       TLS_RSA_WITH_RC4_128_SHA:           &cipherSuite{16, 20, 0, rsaKA, false, cipherRC4, hmacSHA1},
+       TLS_RSA_WITH_AES_128_CBC_SHA:       &cipherSuite{16, 20, 16, rsaKA, false, cipherAES, hmacSHA1},
+       TLS_ECDHE_RSA_WITH_RC4_128_SHA:     &cipherSuite{16, 20, 0, ecdheRSAKA, true, cipherRC4, hmacSHA1},
+       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, ecdheRSAKA, true, cipherAES, hmacSHA1},
 }
 
 func cipherRC4(key, iv []byte, isRead bool) interface{} {
@@ -45,6 +73,14 @@ func hmacSHA1(key []byte) hash.Hash {
        return hmac.NewSHA1(key)
 }
 
+func rsaKA() keyAgreement {
+       return rsaKeyAgreement{}
+}
+
+func ecdheRSAKA() keyAgreement {
+       return new(ecdheRSAKeyAgreement)
+}
+
 // mutualCipherSuite returns a cipherSuite and its id given a list of supported
 // ciphersuites and the id requested by the peer.
 func mutualCipherSuite(have []uint16, want uint16) (suite *cipherSuite, id uint16) {
@@ -59,6 +95,8 @@ func mutualCipherSuite(have []uint16, want uint16) (suite *cipherSuite, id uint1
 // A list of the possible cipher suite ids. Taken from
 // http://www.iana.org/assignments/tls-parameters/tls-parameters.xml
 const (
-       TLS_RSA_WITH_RC4_128_SHA     uint16 = 0x0005
-       TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
+       TLS_RSA_WITH_RC4_128_SHA           uint16 = 0x0005
+       TLS_RSA_WITH_AES_128_CBC_SHA       uint16 = 0x002f
+       TLS_ECDHE_RSA_WITH_RC4_128_SHA     uint16 = 0xc011
+       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
 )
index 6df4264a21ff9d72f79504432a2a99d90f276393..7135f3d0f716853fe89c9364ac5695eb0d956f06 100644 (file)
@@ -38,6 +38,7 @@ const (
        typeClientHello        uint8 = 1
        typeServerHello        uint8 = 2
        typeCertificate        uint8 = 11
+       typeServerKeyExchange  uint8 = 12
        typeCertificateRequest uint8 = 13
        typeServerHelloDone    uint8 = 14
        typeCertificateVerify  uint8 = 15
@@ -54,9 +55,25 @@ const (
 
 // TLS extension numbers
 var (
-       extensionServerName    uint16 = 0
-       extensionStatusRequest uint16 = 5
-       extensionNextProtoNeg  uint16 = 13172 // not IANA assigned
+       extensionServerName      uint16 = 0
+       extensionStatusRequest   uint16 = 5
+       extensionSupportedCurves uint16 = 10
+       extensionSupportedPoints uint16 = 11
+       extensionNextProtoNeg    uint16 = 13172 // not IANA assigned
+)
+
+// TLS Elliptic Curves
+// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8
+var (
+       curveP256 uint16 = 23
+       curveP384 uint16 = 24
+       curveP521 uint16 = 25
+)
+
+// TLS Elliptic Curve Point Formats
+// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9
+var (
+       pointFormatUncompressed uint8 = 0
 )
 
 // TLS CertificateStatusType (RFC 3546)
index 8b1cd86af9c7472ebf89a2c94aab3a125862ed81..d203e8d5169afa1055937886d55da3c46c19f82b 100644 (file)
@@ -651,6 +651,8 @@ func (c *Conn) readHandshake() (interface{}, os.Error) {
                m = new(certificateRequestMsg)
        case typeCertificateStatus:
                m = new(certificateStatusMsg)
+       case typeServerKeyExchange:
+               m = new(serverKeyExchangeMsg)
        case typeServerHelloDone:
                m = new(serverHelloDoneMsg)
        case typeClientKeyExchange:
index e5e8a1f627eec6d97759be1e789c1dff1beaf1c4..1ca33f59dd080cdfccc2c5351e922dd6523b3511 100644 (file)
@@ -26,6 +26,8 @@ func (c *Conn) clientHandshake() os.Error {
                random:             make([]byte, 32),
                ocspStapling:       true,
                serverName:         c.config.ServerName,
+               supportedCurves:    []uint16{curveP256, curveP384, curveP521},
+               supportedPoints:    []uint8{pointFormatUncompressed},
        }
 
        t := uint32(c.config.time())
@@ -130,8 +132,7 @@ func (c *Conn) clientHandshake() os.Error {
                cur = parent
        }
 
-       pub, ok := certs[0].PublicKey.(*rsa.PublicKey)
-       if !ok {
+       if _, ok := certs[0].PublicKey.(*rsa.PublicKey); !ok {
                return c.sendAlert(alertUnsupportedCertificate)
        }
 
@@ -158,6 +159,23 @@ func (c *Conn) clientHandshake() os.Error {
                return err
        }
 
+       keyAgreement := suite.ka()
+
+       skx, ok := msg.(*serverKeyExchangeMsg)
+       if ok {
+               finishedHash.Write(skx.marshal())
+               err = keyAgreement.processServerKeyExchange(c.config, hello, serverHello, certs[0], skx)
+               if err != nil {
+                       c.sendAlert(alertUnexpectedMessage)
+                       return err
+               }
+
+               msg, err = c.readHandshake()
+               if err != nil {
+                       return err
+               }
+       }
+
        transmitCert := false
        certReq, ok := msg.(*certificateRequestMsg)
        if ok {
@@ -215,23 +233,16 @@ func (c *Conn) clientHandshake() os.Error {
                c.writeRecord(recordTypeHandshake, certMsg.marshal())
        }
 
-       ckx := new(clientKeyExchangeMsg)
-       preMasterSecret := make([]byte, 48)
-       preMasterSecret[0] = byte(hello.vers >> 8)
-       preMasterSecret[1] = byte(hello.vers)
-       _, err = io.ReadFull(c.config.rand(), preMasterSecret[2:])
+       preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hello, certs[0])
        if err != nil {
-               return c.sendAlert(alertInternalError)
+               c.sendAlert(alertInternalError)
+               return err
        }
-
-       ckx.ciphertext, err = rsa.EncryptPKCS1v15(c.config.rand(), pub, preMasterSecret)
-       if err != nil {
-               return c.sendAlert(alertInternalError)
+       if ckx != nil {
+               finishedHash.Write(ckx.marshal())
+               c.writeRecord(recordTypeHandshake, ckx.marshal())
        }
 
-       finishedHash.Write(ckx.marshal())
-       c.writeRecord(recordTypeHandshake, ckx.marshal())
-
        if cert != nil {
                certVerify := new(certificateVerifyMsg)
                var digest [36]byte
diff --git a/src/pkg/crypto/tls/handshake_client_test.go b/src/pkg/crypto/tls/handshake_client_test.go
new file mode 100644 (file)
index 0000000..e5c9684
--- /dev/null
@@ -0,0 +1,211 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tls
+
+import (
+       "bytes"
+       "flag"
+       "io"
+       "net"
+       "testing"
+)
+
+func testClientScript(t *testing.T, name string, clientScript [][]byte, config *Config) {
+       c, s := net.Pipe()
+       cli := Client(c, config)
+       go func() {
+               cli.Write([]byte("hello\n"))
+               cli.Close()
+       }()
+
+       defer c.Close()
+       for i, b := range clientScript {
+               if i%2 == 1 {
+                       s.Write(b)
+                       continue
+               }
+               bb := make([]byte, len(b))
+               _, err := io.ReadFull(s, bb)
+               if err != nil {
+                       t.Fatalf("%s #%d: %s", name, i, err)
+               }
+               if !bytes.Equal(b, bb) {
+                       t.Fatalf("%s #%d: mismatch on read: got:%x want:%x", name, i, bb, b)
+               }
+       }
+}
+
+func TestHandshakeClientRC4(t *testing.T) {
+       testClientScript(t, "RC4", rc4ClientScript, testConfig)
+}
+
+var connect = flag.Bool("connect", false, "connect to a TLS server on :10443")
+
+func TestRunClient(t *testing.T) {
+       if !*connect {
+               return
+       }
+
+       testConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA}
+
+       conn, err := Dial("tcp", "", "127.0.0.1:10443", testConfig)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       conn.Write([]byte("hello\n"))
+       conn.Close()
+}
+
+// Script of interaction with gnutls implementation.
+// The values for this test are obtained by building and running in client mode:
+//   % gotest -match "TestRunClient" -connect
+// and then:
+//   % gnutls-serv -p 10443 --debug 100 --x509keyfile key.pem --x509certfile cert.pem -a > /tmp/log 2>&1
+//   % python parse-gnutls-cli-debug-log.py < /tmp/log
+//
+// Where key.pem is:
+// -----BEGIN RSA PRIVATE KEY-----
+// MIIBPAIBAAJBAJ+zw4Qnlf8SMVIPFe9GEcStgOY2Ww/dgNdhjeD8ckUJNP5VZkVD
+// TGiXav6ooKXfX3j/7tdkuD8Ey2//Kv7+ue0CAwEAAQJAN6W31vDEP2DjdqhzCDDu
+// OA4NACqoiFqyblo7yc2tM4h4xMbC3Yx5UKMN9ZkCtX0gzrz6DyF47bdKcWBzNWCj
+// gQIhANEoojVt7hq+SQ6MCN6FTAysGgQf56Q3TYoJMoWvdiXVAiEAw3e3rc+VJpOz
+// rHuDo6bgpjUAAXM+v3fcpsfZSNO6V7kCIQCtbVjanpUwvZkMI9by02oUk9taki3b
+// PzPfAfNPYAbCJQIhAJXNQDWyqwn/lGmR11cqY2y9nZ1+5w3yHGatLrcDnQHxAiEA
+// vnlEGo8K85u+KwIOimM48ZG8oTk7iFdkqLJR1utT3aU=
+// -----END RSA PRIVATE KEY-----
+//
+// and cert.pem is:
+// -----BEGIN CERTIFICATE-----
+// MIIBoDCCAUoCAQAwDQYJKoZIhvcNAQEEBQAwYzELMAkGA1UEBhMCQVUxEzARBgNV
+// BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMSMwIQYD
+// VQQDExpTZXJ2ZXIgdGVzdCBjZXJ0ICg1MTIgYml0KTAeFw05NzA5MDkwMzQxMjZa
+// Fw05NzEwMDkwMzQxMjZaMF4xCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
+// YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxFzAVBgNVBAMT
+// DkVyaWMgdGhlIFlvdW5nMFEwCQYFKw4DAgwFAANEAAJBALVEqPODnpI4rShlY8S7
+// tB713JNvabvn6Gned7zylwLLiXQAo/PAT6mfdWPTyCX9RlId/Aroh1ou893BA32Q
+// sggwDQYJKoZIhvcNAQEEBQADQQCU5SSgapJSdRXJoX+CpCvFy+JVh9HpSjCpSNKO
+// 19raHv98hKAUJuP9HyM+SUsffO6mAIgitUaqW8/wDMePhEC3
+// -----END CERTIFICATE-----
+var rc4ClientScript = [][]byte{
+       {
+               0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00,
+               0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05,
+               0x01, 0x00, 0x00, 0x1b, 0x00, 0x05, 0x00, 0x05,
+               0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
+               0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00,
+               0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00,
+       },
+
+       {
+               0x16, 0x03, 0x01, 0x00, 0x4a, 0x02, 0x00, 0x00,
+               0x46, 0x03, 0x01, 0x4d, 0x0a, 0x56, 0x16, 0xb5,
+               0x91, 0xd1, 0xcb, 0x80, 0x4d, 0xc7, 0x46, 0xf3,
+               0x37, 0x0c, 0xef, 0xea, 0x64, 0x11, 0x14, 0x56,
+               0x97, 0x9b, 0xc5, 0x67, 0x08, 0xb7, 0x13, 0xea,
+               0xf8, 0xc9, 0xb3, 0x20, 0xe2, 0xfc, 0x41, 0xf6,
+               0x96, 0x90, 0x9d, 0x43, 0x9b, 0xe9, 0x6e, 0xf8,
+               0x41, 0x16, 0xcc, 0xf3, 0xc7, 0xde, 0xda, 0x5a,
+               0xa1, 0x33, 0x69, 0xe2, 0xde, 0x5b, 0xaf, 0x2a,
+               0x92, 0xe7, 0xd4, 0xa0, 0x00, 0x05, 0x00, 0x16,
+               0x03, 0x01, 0x01, 0xf7, 0x0b, 0x00, 0x01, 0xf3,
+               0x00, 0x01, 0xf0, 0x00, 0x01, 0xed, 0x30, 0x82,
+               0x01, 0xe9, 0x30, 0x82, 0x01, 0x52, 0x02, 0x01,
+               0x06, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+               0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04, 0x05, 0x00,
+               0x30, 0x5b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+               0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31,
+               0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08,
+               0x13, 0x0a, 0x51, 0x75, 0x65, 0x65, 0x6e, 0x73,
+               0x6c, 0x61, 0x6e, 0x64, 0x31, 0x1a, 0x30, 0x18,
+               0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x43,
+               0x72, 0x79, 0x70, 0x74, 0x53, 0x6f, 0x66, 0x74,
+               0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64,
+               0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04,
+               0x03, 0x13, 0x12, 0x54, 0x65, 0x73, 0x74, 0x20,
+               0x43, 0x41, 0x20, 0x28, 0x31, 0x30, 0x32, 0x34,
+               0x20, 0x62, 0x69, 0x74, 0x29, 0x30, 0x1e, 0x17,
+               0x0d, 0x30, 0x30, 0x31, 0x30, 0x31, 0x36, 0x32,
+               0x32, 0x33, 0x31, 0x30, 0x33, 0x5a, 0x17, 0x0d,
+               0x30, 0x33, 0x30, 0x31, 0x31, 0x34, 0x32, 0x32,
+               0x33, 0x31, 0x30, 0x33, 0x5a, 0x30, 0x63, 0x31,
+               0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+               0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
+               0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x51,
+               0x75, 0x65, 0x65, 0x6e, 0x73, 0x6c, 0x61, 0x6e,
+               0x64, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55,
+               0x04, 0x0a, 0x13, 0x11, 0x43, 0x72, 0x79, 0x70,
+               0x74, 0x53, 0x6f, 0x66, 0x74, 0x20, 0x50, 0x74,
+               0x79, 0x20, 0x4c, 0x74, 0x64, 0x31, 0x23, 0x30,
+               0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a,
+               0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x74,
+               0x65, 0x73, 0x74, 0x20, 0x63, 0x65, 0x72, 0x74,
+               0x20, 0x28, 0x35, 0x31, 0x32, 0x20, 0x62, 0x69,
+               0x74, 0x29, 0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09,
+               0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+               0x01, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30, 0x48,
+               0x02, 0x41, 0x00, 0x9f, 0xb3, 0xc3, 0x84, 0x27,
+               0x95, 0xff, 0x12, 0x31, 0x52, 0x0f, 0x15, 0xef,
+               0x46, 0x11, 0xc4, 0xad, 0x80, 0xe6, 0x36, 0x5b,
+               0x0f, 0xdd, 0x80, 0xd7, 0x61, 0x8d, 0xe0, 0xfc,
+               0x72, 0x45, 0x09, 0x34, 0xfe, 0x55, 0x66, 0x45,
+               0x43, 0x4c, 0x68, 0x97, 0x6a, 0xfe, 0xa8, 0xa0,
+               0xa5, 0xdf, 0x5f, 0x78, 0xff, 0xee, 0xd7, 0x64,
+               0xb8, 0x3f, 0x04, 0xcb, 0x6f, 0xff, 0x2a, 0xfe,
+               0xfe, 0xb9, 0xed, 0x02, 0x03, 0x01, 0x00, 0x01,
+               0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+               0xf7, 0x0d, 0x01, 0x01, 0x04, 0x05, 0x00, 0x03,
+               0x81, 0x81, 0x00, 0x93, 0xd2, 0x0a, 0xc5, 0x41,
+               0xe6, 0x5a, 0xa9, 0x86, 0xf9, 0x11, 0x87, 0xe4,
+               0xdb, 0x45, 0xe2, 0xc5, 0x95, 0x78, 0x1a, 0x6c,
+               0x80, 0x6d, 0x73, 0x1f, 0xb4, 0x6d, 0x44, 0xa3,
+               0xba, 0x86, 0x88, 0xc8, 0x58, 0xcd, 0x1c, 0x06,
+               0x35, 0x6c, 0x44, 0x62, 0x88, 0xdf, 0xe4, 0xf6,
+               0x64, 0x61, 0x95, 0xef, 0x4a, 0xa6, 0x7f, 0x65,
+               0x71, 0xd7, 0x6b, 0x88, 0x39, 0xf6, 0x32, 0xbf,
+               0xac, 0x93, 0x67, 0x69, 0x51, 0x8c, 0x93, 0xec,
+               0x48, 0x5f, 0xc9, 0xb1, 0x42, 0xf9, 0x55, 0xd2,
+               0x7e, 0x4e, 0xf4, 0xf2, 0x21, 0x6b, 0x90, 0x57,
+               0xe6, 0xd7, 0x99, 0x9e, 0x41, 0xca, 0x80, 0xbf,
+               0x1a, 0x28, 0xa2, 0xca, 0x5b, 0x50, 0x4a, 0xed,
+               0x84, 0xe7, 0x82, 0xc7, 0xd2, 0xcf, 0x36, 0x9e,
+               0x6a, 0x67, 0xb9, 0x88, 0xa7, 0xf3, 0x8a, 0xd0,
+               0x04, 0xf8, 0xe8, 0xc6, 0x17, 0xe3, 0xc5, 0x29,
+               0xbc, 0x17, 0xf1, 0x16, 0x03, 0x01, 0x00, 0x04,
+               0x0e, 0x00, 0x00, 0x00,
+       },
+
+       {
+               0x16, 0x03, 0x01, 0x00, 0x46, 0x10, 0x00, 0x00,
+               0x42, 0x00, 0x40, 0x87, 0xa1, 0x1f, 0x14, 0xe1,
+               0xfb, 0x91, 0xac, 0x58, 0x2e, 0xf3, 0x71, 0xce,
+               0x01, 0x85, 0x2c, 0xc7, 0xfe, 0x84, 0x87, 0x82,
+               0xb7, 0x57, 0xdb, 0x37, 0x4d, 0x46, 0x83, 0x67,
+               0x52, 0x82, 0x51, 0x01, 0x95, 0x23, 0x68, 0x69,
+               0x6b, 0xd0, 0xa7, 0xa7, 0xe5, 0x88, 0xd0, 0x47,
+               0x71, 0xb8, 0xd2, 0x03, 0x05, 0x25, 0x56, 0x5c,
+               0x10, 0x08, 0xc6, 0x9b, 0xd4, 0x67, 0xcd, 0x28,
+               0xbe, 0x9c, 0x48, 0x14, 0x03, 0x01, 0x00, 0x01,
+               0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xc1, 0xb8,
+               0xd3, 0x7f, 0xc5, 0xc2, 0x5a, 0x1d, 0x6d, 0x5b,
+               0x2d, 0x5c, 0x82, 0x87, 0xc2, 0x6f, 0x0d, 0x63,
+               0x7b, 0x72, 0x2b, 0xda, 0x69, 0xc4, 0xfe, 0x3c,
+               0x84, 0xa1, 0x5a, 0x62, 0x38, 0x37, 0xc6, 0x54,
+               0x25, 0x2a,
+       },
+
+       {
+               0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
+               0x01, 0x00, 0x24, 0xea, 0x88, 0x9c, 0x00, 0xf6,
+               0x35, 0xb8, 0x42, 0x7f, 0x15, 0x17, 0x76, 0x5e,
+               0x4b, 0x24, 0xcb, 0x7e, 0xa0, 0x7b, 0xc3, 0x70,
+               0x52, 0x0a, 0x88, 0x2a, 0x7a, 0x45, 0x59, 0x90,
+               0x59, 0xac, 0xc6, 0xb5, 0x56, 0x55, 0x96,
+       },
+}
index 91771ce62b138271036c10d539757d18c3866503..e5e8562713df54485c857a481718f4acd84795b3 100644 (file)
@@ -14,6 +14,8 @@ type clientHelloMsg struct {
        nextProtoNeg       bool
        serverName         string
        ocspStapling       bool
+       supportedCurves    []uint16
+       supportedPoints    []uint8
 }
 
 func (m *clientHelloMsg) marshal() []byte {
@@ -35,6 +37,14 @@ func (m *clientHelloMsg) marshal() []byte {
                extensionsLength += 5 + len(m.serverName)
                numExtensions++
        }
+       if len(m.supportedCurves) > 0 {
+               extensionsLength += 2 + 2*len(m.supportedCurves)
+               numExtensions++
+       }
+       if len(m.supportedPoints) > 0 {
+               extensionsLength += 1 + len(m.supportedPoints)
+               numExtensions++
+       }
        if numExtensions > 0 {
                extensionsLength += 4 * numExtensions
                length += 2 + extensionsLength
@@ -117,6 +127,38 @@ func (m *clientHelloMsg) marshal() []byte {
                // Two zero valued uint16s for the two lengths.
                z = z[9:]
        }
+       if len(m.supportedCurves) > 0 {
+               // http://tools.ietf.org/html/rfc4492#section-5.5.1
+               z[0] = byte(extensionSupportedCurves >> 8)
+               z[1] = byte(extensionSupportedCurves)
+               l := 2 + 2*len(m.supportedCurves)
+               z[2] = byte(l >> 8)
+               z[3] = byte(l)
+               l -= 2
+               z[4] = byte(l >> 8)
+               z[5] = byte(l)
+               z = z[6:]
+               for _, curve := range m.supportedCurves {
+                       z[0] = byte(curve >> 8)
+                       z[1] = byte(curve)
+                       z = z[2:]
+               }
+       }
+       if len(m.supportedPoints) > 0 {
+               // http://tools.ietf.org/html/rfc4492#section-5.5.2
+               z[0] = byte(extensionSupportedPoints >> 8)
+               z[1] = byte(extensionSupportedPoints)
+               l := 1 + len(m.supportedPoints)
+               z[2] = byte(l >> 8)
+               z[3] = byte(l)
+               l--
+               z[4] = byte(l)
+               z = z[5:]
+               for _, pointFormat := range m.supportedPoints {
+                       z[0] = byte(pointFormat)
+                       z = z[1:]
+               }
+       }
 
        m.raw = x
 
@@ -221,6 +263,33 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
                        m.nextProtoNeg = true
                case extensionStatusRequest:
                        m.ocspStapling = length > 0 && data[0] == statusTypeOCSP
+               case extensionSupportedCurves:
+                       // http://tools.ietf.org/html/rfc4492#section-5.5.1
+                       if length < 2 {
+                               return false
+                       }
+                       l := int(data[0])<<8 | int(data[1])
+                       if l%2 == 1 || length != l+2 {
+                               return false
+                       }
+                       numCurves := l / 2
+                       m.supportedCurves = make([]uint16, numCurves)
+                       d := data[2:]
+                       for i := 0; i < numCurves; i++ {
+                               m.supportedCurves[i] = uint16(d[0])<<8 | uint16(d[1])
+                               d = d[2:]
+                       }
+               case extensionSupportedPoints:
+                       // http://tools.ietf.org/html/rfc4492#section-5.5.2
+                       if length < 1 {
+                               return false
+                       }
+                       l := int(data[0])
+                       if length != l+1 {
+                               return false
+                       }
+                       m.supportedPoints = make([]uint8, l)
+                       copy(m.supportedPoints, data[1:])
                }
                data = data[length:]
        }
@@ -466,6 +535,36 @@ func (m *certificateMsg) unmarshal(data []byte) bool {
        return true
 }
 
+type serverKeyExchangeMsg struct {
+       raw []byte
+       key []byte
+}
+
+func (m *serverKeyExchangeMsg) marshal() []byte {
+       if m.raw != nil {
+               return m.raw
+       }
+       length := len(m.key)
+       x := make([]byte, length+4)
+       x[0] = typeServerKeyExchange
+       x[1] = uint8(length >> 16)
+       x[2] = uint8(length >> 8)
+       x[3] = uint8(length)
+       copy(x[4:], m.key)
+
+       m.raw = x
+       return x
+}
+
+func (m *serverKeyExchangeMsg) unmarshal(data []byte) bool {
+       m.raw = data
+       if len(data) < 4 {
+               return false
+       }
+       m.key = data[4:]
+       return true
+}
+
 type certificateStatusMsg struct {
        raw        []byte
        statusType uint8
@@ -542,15 +641,13 @@ func (m *clientKeyExchangeMsg) marshal() []byte {
        if m.raw != nil {
                return m.raw
        }
-       length := len(m.ciphertext) + 2
+       length := len(m.ciphertext)
        x := make([]byte, length+4)
        x[0] = typeClientKeyExchange
        x[1] = uint8(length >> 16)
        x[2] = uint8(length >> 8)
        x[3] = uint8(length)
-       x[4] = uint8(len(m.ciphertext) >> 8)
-       x[5] = uint8(len(m.ciphertext))
-       copy(x[6:], m.ciphertext)
+       copy(x[4:], m.ciphertext)
 
        m.raw = x
        return x
@@ -558,14 +655,14 @@ func (m *clientKeyExchangeMsg) marshal() []byte {
 
 func (m *clientKeyExchangeMsg) unmarshal(data []byte) bool {
        m.raw = data
-       if len(data) < 7 {
+       if len(data) < 4 {
                return false
        }
-       cipherTextLen := int(data[4])<<8 | int(data[5])
-       if len(data) != 6+cipherTextLen {
+       l := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
+       if l != len(data)-4 {
                return false
        }
-       m.ciphertext = data[6:]
+       m.ciphertext = data[4:]
        return true
 }
 
index 64d23e06ca196daf3f903c03e734f94ea9f17fb7..21577dd0b0176587b2e133da72dedcc715446053 100644 (file)
@@ -115,6 +115,11 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value {
                m.serverName = randomString(rand.Intn(255), rand)
        }
        m.ocspStapling = rand.Intn(10) > 5
+       m.supportedPoints = randomBytes(rand.Intn(5)+1, rand)
+       m.supportedCurves = make([]uint16, rand.Intn(5)+1)
+       for i, _ := range m.supportedCurves {
+               m.supportedCurves[i] = uint16(rand.Intn(30000))
+       }
 
        return reflect.NewValue(m)
 }
index 29c8aadb40a537fc4437083bcafdd4e95499c373..955811ada35d3b3aceb89d6133135b99bd0ef60a 100644 (file)
@@ -34,12 +34,37 @@ func (c *Conn) serverHandshake() os.Error {
 
        hello := new(serverHelloMsg)
 
+       supportedCurve := false
+Curves:
+       for _, curve := range clientHello.supportedCurves {
+               switch curve {
+               case curveP256, curveP384, curveP521:
+                       supportedCurve = true
+                       break Curves
+               }
+       }
+
+       supportedPointFormat := false
+       for _, pointFormat := range clientHello.supportedPoints {
+               if pointFormat == pointFormatUncompressed {
+                       supportedPointFormat = true
+                       break
+               }
+       }
+
+       ellipticOk := supportedCurve && supportedPointFormat
+
        var suite *cipherSuite
        var suiteId uint16
        for _, id := range clientHello.cipherSuites {
                for _, supported := range config.cipherSuites() {
                        if id == supported {
                                suite = cipherSuites[id]
+                               // Don't select a ciphersuite which we can't
+                               // support for this client.
+                               if suite.elliptic && !ellipticOk {
+                                       continue
+                               }
                                suiteId = id
                                break
                        }
@@ -89,6 +114,18 @@ func (c *Conn) serverHandshake() os.Error {
        finishedHash.Write(certMsg.marshal())
        c.writeRecord(recordTypeHandshake, certMsg.marshal())
 
+       keyAgreement := suite.ka()
+
+       skx, err := keyAgreement.generateServerKeyExchange(config, clientHello, hello)
+       if err != nil {
+               c.sendAlert(alertHandshakeFailure)
+               return err
+       }
+       if skx != nil {
+               finishedHash.Write(skx.marshal())
+               c.writeRecord(recordTypeHandshake, skx.marshal())
+       }
+
        if config.AuthenticateClient {
                // Request a client certificate
                certReq := new(certificateRequestMsg)
@@ -185,22 +222,11 @@ func (c *Conn) serverHandshake() os.Error {
                finishedHash.Write(certVerify.marshal())
        }
 
-       preMasterSecret := make([]byte, 48)
-       _, err = io.ReadFull(config.rand(), preMasterSecret[2:])
-       if err != nil {
-               return c.sendAlert(alertInternalError)
-       }
-
-       err = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey, ckx.ciphertext, preMasterSecret)
+       preMasterSecret, err := keyAgreement.processClientKeyExchange(config, ckx)
        if err != nil {
-               return c.sendAlert(alertHandshakeFailure)
+               c.sendAlert(alertHandshakeFailure)
+               return err
        }
-       // We don't check the version number in the premaster secret. For one,
-       // by checking it, we would leak information about the validity of the
-       // encrypted pre-master secret. Secondly, it provides only a small
-       // benefit against a downgrade attack and some implementations send the
-       // wrong version anyway. See the discussion at the end of section
-       // 7.4.7.1 of RFC 4346.
 
        masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
                keysFromPreMasterSecret10(preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen)
diff --git a/src/pkg/crypto/tls/key_agreement.go b/src/pkg/crypto/tls/key_agreement.go
new file mode 100644 (file)
index 0000000..861c64f
--- /dev/null
@@ -0,0 +1,246 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tls
+
+import (
+       "big"
+       "crypto/elliptic"
+       "crypto/md5"
+       "crypto/rsa"
+       "crypto/sha1"
+       "crypto/x509"
+       "io"
+       "os"
+)
+
+// rsaKeyAgreement implements the standard TLS key agreement where the client
+// encrypts the pre-master secret to the server's public key.
+type rsaKeyAgreement struct{}
+
+func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, os.Error) {
+       return nil, nil
+}
+
+func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg) ([]byte, os.Error) {
+       preMasterSecret := make([]byte, 48)
+       _, err := io.ReadFull(config.rand(), preMasterSecret[2:])
+       if err != nil {
+               return nil, err
+       }
+
+       if len(ckx.ciphertext) < 2 {
+               return nil, os.ErrorString("bad ClientKeyExchange")
+       }
+       ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1])
+       if ciphertextLen != len(ckx.ciphertext)-2 {
+               return nil, os.ErrorString("bad ClientKeyExchange")
+       }
+       ciphertext := ckx.ciphertext[2:]
+
+       err = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey, ciphertext, preMasterSecret)
+       if err != nil {
+               return nil, err
+       }
+       // We don't check the version number in the premaster secret.  For one,
+       // by checking it, we would leak information about the validity of the
+       // encrypted pre-master secret. Secondly, it provides only a small
+       // benefit against a downgrade attack and some implementations send the
+       // wrong version anyway. See the discussion at the end of section
+       // 7.4.7.1 of RFC 4346.
+       return preMasterSecret, nil
+}
+
+func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) os.Error {
+       return os.ErrorString("unexpected ServerKeyExchange")
+}
+
+func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, os.Error) {
+       preMasterSecret := make([]byte, 48)
+       preMasterSecret[0] = byte(clientHello.vers >> 8)
+       preMasterSecret[1] = byte(clientHello.vers)
+       _, err := io.ReadFull(config.rand(), preMasterSecret[2:])
+       if err != nil {
+               return nil, nil, err
+       }
+
+       encrypted, err := rsa.EncryptPKCS1v15(config.rand(), cert.PublicKey.(*rsa.PublicKey), preMasterSecret)
+       if err != nil {
+               return nil, nil, err
+       }
+       ckx := new(clientKeyExchangeMsg)
+       ckx.ciphertext = make([]byte, len(encrypted)+2)
+       ckx.ciphertext[0] = byte(len(encrypted) >> 8)
+       ckx.ciphertext[1] = byte(len(encrypted))
+       copy(ckx.ciphertext[2:], encrypted)
+       return preMasterSecret, ckx, nil
+}
+
+
+// md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the
+// concatenation of an MD5 and SHA1 hash.
+func md5SHA1Hash(slices ...[]byte) []byte {
+       md5sha1 := make([]byte, md5.Size+sha1.Size)
+       hmd5 := md5.New()
+       for _, slice := range slices {
+               hmd5.Write(slice)
+       }
+       copy(md5sha1, hmd5.Sum())
+
+       hsha1 := sha1.New()
+       for _, slice := range slices {
+               hsha1.Write(slice)
+       }
+       copy(md5sha1[md5.Size:], hsha1.Sum())
+       return md5sha1
+}
+
+// ecdheRSAKeyAgreement implements a TLS key agreement where the server
+// generates a ephemeral EC public/private key pair and signs it. The
+// pre-master secret is then calculated using ECDH.
+type ecdheRSAKeyAgreement struct {
+       privateKey []byte
+       curve      *elliptic.Curve
+       x, y       *big.Int
+}
+
+func (ka *ecdheRSAKeyAgreement) generateServerKeyExchange(config *Config, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, os.Error) {
+       var curveid uint16
+
+Curve:
+       for _, c := range clientHello.supportedCurves {
+               switch c {
+               case curveP256:
+                       ka.curve = elliptic.P256()
+                       curveid = c
+                       break Curve
+               case curveP384:
+                       ka.curve = elliptic.P384()
+                       curveid = c
+                       break Curve
+               case curveP521:
+                       ka.curve = elliptic.P521()
+                       curveid = c
+                       break Curve
+               }
+       }
+
+       var x, y *big.Int
+       var err os.Error
+       ka.privateKey, x, y, err = ka.curve.GenerateKey(config.rand())
+       if err != nil {
+               return nil, err
+       }
+       ecdhePublic := ka.curve.Marshal(x, y)
+
+       // http://tools.ietf.org/html/rfc4492#section-5.4
+       serverECDHParams := make([]byte, 1+2+1+len(ecdhePublic))
+       serverECDHParams[0] = 3 // named curve
+       serverECDHParams[1] = byte(curveid >> 8)
+       serverECDHParams[2] = byte(curveid)
+       serverECDHParams[3] = byte(len(ecdhePublic))
+       copy(serverECDHParams[4:], ecdhePublic)
+
+       md5sha1 := md5SHA1Hash(clientHello.random, hello.random, serverECDHParams)
+       sig, err := rsa.SignPKCS1v15(config.rand(), config.Certificates[0].PrivateKey, rsa.HashMD5SHA1, md5sha1)
+       if err != nil {
+               return nil, os.ErrorString("failed to sign ECDHE parameters: " + err.String())
+       }
+
+       skx := new(serverKeyExchangeMsg)
+       skx.key = make([]byte, len(serverECDHParams)+2+len(sig))
+       copy(skx.key, serverECDHParams)
+       k := skx.key[len(serverECDHParams):]
+       k[0] = byte(len(sig) >> 8)
+       k[1] = byte(len(sig))
+       copy(k[2:], sig)
+
+       return skx, nil
+}
+
+func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg) ([]byte, os.Error) {
+       if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
+               return nil, os.ErrorString("bad ClientKeyExchange")
+       }
+       x, y := ka.curve.Unmarshal(ckx.ciphertext[1:])
+       if x == nil {
+               return nil, os.ErrorString("bad ClientKeyExchange")
+       }
+       x, _ = ka.curve.ScalarMult(x, y, ka.privateKey)
+       preMasterSecret := make([]byte, (ka.curve.BitSize+7)>>3)
+       xBytes := x.Bytes()
+       copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes)
+
+       return preMasterSecret, nil
+}
+
+func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) os.Error {
+       if len(skx.key) < 4 {
+               goto Error
+       }
+       if skx.key[0] != 3 { // named curve
+               return os.ErrorString("server selected unsupported curve")
+       }
+       curveid := uint16(skx.key[1])<<8 | uint16(skx.key[2])
+
+       switch curveid {
+       case curveP256:
+               ka.curve = elliptic.P256()
+       case curveP384:
+               ka.curve = elliptic.P384()
+       case curveP521:
+               ka.curve = elliptic.P521()
+       default:
+               return os.ErrorString("server selected unsupported curve")
+       }
+
+       publicLen := int(skx.key[3])
+       if publicLen+4 > len(skx.key) {
+               goto Error
+       }
+       ka.x, ka.y = ka.curve.Unmarshal(skx.key[4 : 4+publicLen])
+       if ka.x == nil {
+               goto Error
+       }
+       serverECDHParams := skx.key[:4+publicLen]
+
+       sig := skx.key[4+publicLen:]
+       if len(sig) < 2 {
+               goto Error
+       }
+       sigLen := int(sig[0])<<8 | int(sig[1])
+       if sigLen+2 != len(sig) {
+               goto Error
+       }
+       sig = sig[2:]
+
+       md5sha1 := md5SHA1Hash(clientHello.random, serverHello.random, serverECDHParams)
+       return rsa.VerifyPKCS1v15(cert.PublicKey.(*rsa.PublicKey), rsa.HashMD5SHA1, md5sha1, sig)
+
+Error:
+       return os.ErrorString("invalid ServerKeyExchange")
+}
+
+func (ka *ecdheRSAKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, os.Error) {
+       if ka.curve == nil {
+               return nil, nil, os.ErrorString("missing ServerKeyExchange message")
+       }
+       priv, mx, my, err := ka.curve.GenerateKey(config.rand())
+       if err != nil {
+               return nil, nil, err
+       }
+       x, _ := ka.curve.ScalarMult(ka.x, ka.y, priv)
+       preMasterSecret := make([]byte, (ka.curve.BitSize+7)>>3)
+       xBytes := x.Bytes()
+       copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes)
+
+       serialised := ka.curve.Marshal(mx, my)
+
+       ckx := new(clientKeyExchangeMsg)
+       ckx.ciphertext = make([]byte, 1+len(serialised))
+       ckx.ciphertext[0] = byte(len(serialised))
+       copy(ckx.ciphertext[1:], serialised)
+
+       return preMasterSecret, ckx, nil
+}