(ECDHE is "Elliptic Curve Diffie Hellman Ephemeral") R=rsc CC=golang-dev https://golang.org/cl/3668042v1.2.3
@@ -14,6 +14,7 @@ GOFILES=\ | |||
handshake_client.go\ | |||
handshake_messages.go\ | |||
handshake_server.go\ | |||
key_agreement.go\ | |||
prf.go\ | |||
tls.go\ | |||
@@ -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 | |||
) |
@@ -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) | |||
@@ -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: | |||
@@ -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 | |||
@@ -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, | |||
}, | |||
} |
@@ -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 | |||
} | |||
@@ -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) | |||
} | |||
@@ -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) | |||
@@ -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 | |||
} |