crypto/tls: add ALPN support.

Fixes #6736.

LGTM=mikioh.mikioh
R=bradfitz, mikioh.mikioh
CC=golang-codereviews
https://golang.org/cl/108710046
This commit is contained in:
Adam Langley 2014-08-05 11:36:20 -07:00
parent 81674f3135
commit d0e255f259
11 changed files with 701 additions and 21 deletions

View file

@ -9,7 +9,6 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"errors"
@ -258,6 +257,10 @@ type serverTest struct {
expectedPeerCerts []string
// config, if not nil, contains a custom Config to use for this test.
config *Config
// validate, if not nil, is a function that will be called with the
// ConnectionState of the resulting connection. It returns false if the
// ConnectionState is unacceptable.
validate func(ConnectionState) error
}
var defaultClientCommand = []string{"openssl", "s_client", "-no_ticket"}
@ -354,14 +357,14 @@ func (test *serverTest) run(t *testing.T, write bool) {
config = testConfig
}
server := Server(serverConn, config)
peerCertsChan := make(chan []*x509.Certificate, 1)
connStateChan := make(chan ConnectionState, 1)
go func() {
if _, err := server.Write([]byte("hello, world\n")); err != nil {
t.Logf("Error from Server.Write: %s", err)
}
server.Close()
serverConn.Close()
peerCertsChan <- server.ConnectionState().PeerCertificates
connStateChan <- server.ConnectionState()
}()
if !write {
@ -386,7 +389,8 @@ func (test *serverTest) run(t *testing.T, write bool) {
clientConn.Close()
}
peerCerts := <-peerCertsChan
connState := <-connStateChan
peerCerts := connState.PeerCertificates
if len(peerCerts) == len(test.expectedPeerCerts) {
for i, peerCert := range peerCerts {
block, _ := pem.Decode([]byte(test.expectedPeerCerts[i]))
@ -398,6 +402,12 @@ func (test *serverTest) run(t *testing.T, write bool) {
t.Fatalf("%s: mismatch on peer list length: %d (wanted) != %d (got)", test.name, len(test.expectedPeerCerts), len(peerCerts))
}
if test.validate != nil {
if err := test.validate(connState); err != nil {
t.Fatalf("validate callback returned error: %s", err)
}
}
if write {
path := test.dataPath()
out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
@ -498,6 +508,49 @@ func TestHandshakeServerECDHEECDSAAES(t *testing.T) {
runServerTestTLS12(t, test)
}
func TestHandshakeServerALPN(t *testing.T) {
config := *testConfig
config.NextProtos = []string{"proto1", "proto2"}
test := &serverTest{
name: "ALPN",
// Note that this needs OpenSSL 1.0.2 because that is the first
// version that supports the -alpn flag.
command: []string{"openssl", "s_client", "-alpn", "proto2,proto1"},
config: &config,
validate: func(state ConnectionState) error {
// The server's preferences should override the client.
if state.NegotiatedProtocol != "proto1" {
return fmt.Errorf("Got protocol %q, wanted proto1", state.NegotiatedProtocol)
}
return nil
},
}
runServerTestTLS12(t, test)
}
func TestHandshakeServerALPNNoMatch(t *testing.T) {
config := *testConfig
config.NextProtos = []string{"proto3"}
test := &serverTest{
name: "ALPN-NoMatch",
// Note that this needs OpenSSL 1.0.2 because that is the first
// version that supports the -alpn flag.
command: []string{"openssl", "s_client", "-alpn", "proto2,proto1"},
config: &config,
validate: func(state ConnectionState) error {
// Rather than reject the connection, Go doesn't select
// a protocol when there is no overlap.
if state.NegotiatedProtocol != "" {
return fmt.Errorf("Got protocol %q, wanted ''", state.NegotiatedProtocol)
}
return nil
},
}
runServerTestTLS12(t, test)
}
// TestHandshakeServerSNI involves a client sending an SNI extension of
// "snitest.com", which happens to match the CN of testSNICertificate. The test
// verifies that the server correctly selects that certificate.