1
0
mirror of https://github.com/golang/go synced 2024-11-25 07:07:57 -07:00

crypto/tls: add ECDHE support

(ECDHE is "Elliptic Curve Diffie Hellman Ephemeral")

R=rsc
CC=golang-dev
https://golang.org/cl/3668042
This commit is contained in:
Adam Langley 2010-12-16 17:10:50 -05:00
parent 2e8be52ff7
commit 4883b73982
11 changed files with 703 additions and 46 deletions

View File

@ -130,6 +130,9 @@ func nonZeroRandomBytes(s []byte, rand io.Reader) (err os.Error) {
if err != nil { if err != nil {
return return
} }
// In tests, the PRNG may return all zeros so we do
// this to break the loop.
s[i] ^= 0x42
} }
} }

View File

@ -14,6 +14,7 @@ GOFILES=\
handshake_client.go\ handshake_client.go\
handshake_messages.go\ handshake_messages.go\
handshake_server.go\ handshake_server.go\
key_agreement.go\
prf.go\ prf.go\
tls.go\ tls.go\

View File

@ -9,9 +9,30 @@ import (
"crypto/cipher" "crypto/cipher"
"crypto/hmac" "crypto/hmac"
"crypto/rc4" "crypto/rc4"
"crypto/x509"
"hash" "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 // A cipherSuite is a specific combination of key agreement, cipher and MAC
// function. All cipher suites currently assume RSA key agreement. // function. All cipher suites currently assume RSA key agreement.
type cipherSuite struct { type cipherSuite struct {
@ -19,13 +40,20 @@ type cipherSuite struct {
keyLen int keyLen int
macLen int macLen int
ivLen int ivLen int
cipher func(key, iv []byte, isRead bool) interface{} ka func() keyAgreement
mac func(macKey []byte) hash.Hash // 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{ var cipherSuites = map[uint16]*cipherSuite{
TLS_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, cipherRC4, 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, cipherAES, 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{} { func cipherRC4(key, iv []byte, isRead bool) interface{} {
@ -45,6 +73,14 @@ func hmacSHA1(key []byte) hash.Hash {
return hmac.NewSHA1(key) 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 // mutualCipherSuite returns a cipherSuite and its id given a list of supported
// ciphersuites and the id requested by the peer. // ciphersuites and the id requested by the peer.
func mutualCipherSuite(have []uint16, want uint16) (suite *cipherSuite, id uint16) { 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 // A list of the possible cipher suite ids. Taken from
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml // http://www.iana.org/assignments/tls-parameters/tls-parameters.xml
const ( const (
TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f 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
) )

View File

@ -38,6 +38,7 @@ const (
typeClientHello uint8 = 1 typeClientHello uint8 = 1
typeServerHello uint8 = 2 typeServerHello uint8 = 2
typeCertificate uint8 = 11 typeCertificate uint8 = 11
typeServerKeyExchange uint8 = 12
typeCertificateRequest uint8 = 13 typeCertificateRequest uint8 = 13
typeServerHelloDone uint8 = 14 typeServerHelloDone uint8 = 14
typeCertificateVerify uint8 = 15 typeCertificateVerify uint8 = 15
@ -54,9 +55,25 @@ const (
// TLS extension numbers // TLS extension numbers
var ( var (
extensionServerName uint16 = 0 extensionServerName uint16 = 0
extensionStatusRequest uint16 = 5 extensionStatusRequest uint16 = 5
extensionNextProtoNeg uint16 = 13172 // not IANA assigned 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) // TLS CertificateStatusType (RFC 3546)

View File

@ -651,6 +651,8 @@ func (c *Conn) readHandshake() (interface{}, os.Error) {
m = new(certificateRequestMsg) m = new(certificateRequestMsg)
case typeCertificateStatus: case typeCertificateStatus:
m = new(certificateStatusMsg) m = new(certificateStatusMsg)
case typeServerKeyExchange:
m = new(serverKeyExchangeMsg)
case typeServerHelloDone: case typeServerHelloDone:
m = new(serverHelloDoneMsg) m = new(serverHelloDoneMsg)
case typeClientKeyExchange: case typeClientKeyExchange:

View File

@ -26,6 +26,8 @@ func (c *Conn) clientHandshake() os.Error {
random: make([]byte, 32), random: make([]byte, 32),
ocspStapling: true, ocspStapling: true,
serverName: c.config.ServerName, serverName: c.config.ServerName,
supportedCurves: []uint16{curveP256, curveP384, curveP521},
supportedPoints: []uint8{pointFormatUncompressed},
} }
t := uint32(c.config.time()) t := uint32(c.config.time())
@ -130,8 +132,7 @@ func (c *Conn) clientHandshake() os.Error {
cur = parent cur = parent
} }
pub, ok := certs[0].PublicKey.(*rsa.PublicKey) if _, ok := certs[0].PublicKey.(*rsa.PublicKey); !ok {
if !ok {
return c.sendAlert(alertUnsupportedCertificate) return c.sendAlert(alertUnsupportedCertificate)
} }
@ -158,6 +159,23 @@ func (c *Conn) clientHandshake() os.Error {
return err 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 transmitCert := false
certReq, ok := msg.(*certificateRequestMsg) certReq, ok := msg.(*certificateRequestMsg)
if ok { if ok {
@ -215,23 +233,16 @@ func (c *Conn) clientHandshake() os.Error {
c.writeRecord(recordTypeHandshake, certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal())
} }
ckx := new(clientKeyExchangeMsg) preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hello, certs[0])
preMasterSecret := make([]byte, 48)
preMasterSecret[0] = byte(hello.vers >> 8)
preMasterSecret[1] = byte(hello.vers)
_, err = io.ReadFull(c.config.rand(), preMasterSecret[2:])
if err != nil { if err != nil {
return c.sendAlert(alertInternalError) c.sendAlert(alertInternalError)
return err
} }
if ckx != nil {
ckx.ciphertext, err = rsa.EncryptPKCS1v15(c.config.rand(), pub, preMasterSecret) finishedHash.Write(ckx.marshal())
if err != nil { c.writeRecord(recordTypeHandshake, ckx.marshal())
return c.sendAlert(alertInternalError)
} }
finishedHash.Write(ckx.marshal())
c.writeRecord(recordTypeHandshake, ckx.marshal())
if cert != nil { if cert != nil {
certVerify := new(certificateVerifyMsg) certVerify := new(certificateVerifyMsg)
var digest [36]byte var digest [36]byte

View File

@ -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,
},
}

View File

@ -14,6 +14,8 @@ type clientHelloMsg struct {
nextProtoNeg bool nextProtoNeg bool
serverName string serverName string
ocspStapling bool ocspStapling bool
supportedCurves []uint16
supportedPoints []uint8
} }
func (m *clientHelloMsg) marshal() []byte { func (m *clientHelloMsg) marshal() []byte {
@ -35,6 +37,14 @@ func (m *clientHelloMsg) marshal() []byte {
extensionsLength += 5 + len(m.serverName) extensionsLength += 5 + len(m.serverName)
numExtensions++ 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 { if numExtensions > 0 {
extensionsLength += 4 * numExtensions extensionsLength += 4 * numExtensions
length += 2 + extensionsLength length += 2 + extensionsLength
@ -117,6 +127,38 @@ func (m *clientHelloMsg) marshal() []byte {
// Two zero valued uint16s for the two lengths. // Two zero valued uint16s for the two lengths.
z = z[9:] 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 m.raw = x
@ -221,6 +263,33 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
m.nextProtoNeg = true m.nextProtoNeg = true
case extensionStatusRequest: case extensionStatusRequest:
m.ocspStapling = length > 0 && data[0] == statusTypeOCSP 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:] data = data[length:]
} }
@ -466,6 +535,36 @@ func (m *certificateMsg) unmarshal(data []byte) bool {
return true 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 { type certificateStatusMsg struct {
raw []byte raw []byte
statusType uint8 statusType uint8
@ -542,15 +641,13 @@ func (m *clientKeyExchangeMsg) marshal() []byte {
if m.raw != nil { if m.raw != nil {
return m.raw return m.raw
} }
length := len(m.ciphertext) + 2 length := len(m.ciphertext)
x := make([]byte, length+4) x := make([]byte, length+4)
x[0] = typeClientKeyExchange x[0] = typeClientKeyExchange
x[1] = uint8(length >> 16) x[1] = uint8(length >> 16)
x[2] = uint8(length >> 8) x[2] = uint8(length >> 8)
x[3] = uint8(length) x[3] = uint8(length)
x[4] = uint8(len(m.ciphertext) >> 8) copy(x[4:], m.ciphertext)
x[5] = uint8(len(m.ciphertext))
copy(x[6:], m.ciphertext)
m.raw = x m.raw = x
return x return x
@ -558,14 +655,14 @@ func (m *clientKeyExchangeMsg) marshal() []byte {
func (m *clientKeyExchangeMsg) unmarshal(data []byte) bool { func (m *clientKeyExchangeMsg) unmarshal(data []byte) bool {
m.raw = data m.raw = data
if len(data) < 7 { if len(data) < 4 {
return false return false
} }
cipherTextLen := int(data[4])<<8 | int(data[5]) l := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
if len(data) != 6+cipherTextLen { if l != len(data)-4 {
return false return false
} }
m.ciphertext = data[6:] m.ciphertext = data[4:]
return true return true
} }

View File

@ -115,6 +115,11 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value {
m.serverName = randomString(rand.Intn(255), rand) m.serverName = randomString(rand.Intn(255), rand)
} }
m.ocspStapling = rand.Intn(10) > 5 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) return reflect.NewValue(m)
} }

View File

@ -34,12 +34,37 @@ func (c *Conn) serverHandshake() os.Error {
hello := new(serverHelloMsg) 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 suite *cipherSuite
var suiteId uint16 var suiteId uint16
for _, id := range clientHello.cipherSuites { for _, id := range clientHello.cipherSuites {
for _, supported := range config.cipherSuites() { for _, supported := range config.cipherSuites() {
if id == supported { if id == supported {
suite = cipherSuites[id] suite = cipherSuites[id]
// Don't select a ciphersuite which we can't
// support for this client.
if suite.elliptic && !ellipticOk {
continue
}
suiteId = id suiteId = id
break break
} }
@ -89,6 +114,18 @@ func (c *Conn) serverHandshake() os.Error {
finishedHash.Write(certMsg.marshal()) finishedHash.Write(certMsg.marshal())
c.writeRecord(recordTypeHandshake, 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 { if config.AuthenticateClient {
// Request a client certificate // Request a client certificate
certReq := new(certificateRequestMsg) certReq := new(certificateRequestMsg)
@ -185,23 +222,12 @@ func (c *Conn) serverHandshake() os.Error {
finishedHash.Write(certVerify.marshal()) finishedHash.Write(certVerify.marshal())
} }
preMasterSecret := make([]byte, 48) preMasterSecret, err := keyAgreement.processClientKeyExchange(config, ckx)
_, err = io.ReadFull(config.rand(), preMasterSecret[2:])
if err != nil { if err != nil {
return c.sendAlert(alertInternalError) c.sendAlert(alertHandshakeFailure)
return err
} }
err = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey, ckx.ciphertext, preMasterSecret)
if err != nil {
return c.sendAlert(alertHandshakeFailure)
}
// 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 := masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
keysFromPreMasterSecret10(preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen) keysFromPreMasterSecret10(preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen)

View File

@ -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
}