crypto/tls (part 3)

(With hindsight, record_process might have been designed wrong, but it
works for now. It'll get redrawn when client support is added.)

R=rsc
CC=r
http://go/go-review/1018032
This commit is contained in:
Adam Langley 2009-11-05 15:44:32 -08:00
parent 7cfc31151a
commit ebe78b393d
4 changed files with 871 additions and 0 deletions

232
handshake_server.go Normal file
View File

@ -0,0 +1,232 @@
// Copyright 2009 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
// The handshake goroutine reads handshake messages from the record processor
// and outputs messages to be written on another channel. It updates the record
// processor with the state of the connection via the control channel. In the
// case of handshake messages that need synchronous processing (because they
// affect the handling of the next record) the record processor knows about
// them and either waits for a control message (Finished) or includes a reply
// channel in the message (ChangeCipherSpec).
import (
"crypto/hmac";
"crypto/rc4";
"crypto/rsa";
"crypto/sha1";
"crypto/subtle";
"io";
)
type cipherSuite struct {
id uint16; // The number of this suite on the wire.
hashLength, cipherKeyLength int;
// TODO(agl): need a method to create the cipher and hash interfaces.
}
var cipherSuites = []cipherSuite{
cipherSuite{TLS_RSA_WITH_RC4_128_SHA, 20, 16},
}
// A serverHandshake performs the server side of the TLS 1.1 handshake protocol.
type serverHandshake struct {
writeChan chan<- interface{};
controlChan chan<- interface{};
msgChan <-chan interface{};
config *Config;
}
func (h *serverHandshake) loop(writeChan chan<- interface{}, controlChan chan<- interface{}, msgChan <-chan interface{}, config *Config) {
h.writeChan = writeChan;
h.controlChan = controlChan;
h.msgChan = msgChan;
h.config = config;
defer close(writeChan);
defer close(controlChan);
clientHello, ok := h.readHandshakeMsg().(*clientHelloMsg);
if !ok {
h.error(alertUnexpectedMessage);
return;
}
major, minor, ok := mutualVersion(clientHello.major, clientHello.minor);
if !ok {
h.error(alertProtocolVersion);
return;
}
finishedHash := newFinishedHash();
finishedHash.Write(clientHello.marshal());
hello := new(serverHelloMsg);
// We only support a single ciphersuite so we look for it in the list
// of client supported suites.
//
// TODO(agl): Add additional cipher suites.
var suite *cipherSuite;
for _, id := range clientHello.cipherSuites {
for _, supported := range cipherSuites {
if supported.id == id {
suite = &supported;
break;
}
}
}
foundCompression := false;
// We only support null compression, so check that the client offered it.
for _, compression := range clientHello.compressionMethods {
if compression == compressionNone {
foundCompression = true;
break;
}
}
if suite == nil || !foundCompression {
h.error(alertHandshakeFailure);
return;
}
hello.major = major;
hello.minor = minor;
hello.cipherSuite = suite.id;
currentTime := uint32(config.Time());
hello.random = make([]byte, 32);
hello.random[0] = byte(currentTime >> 24);
hello.random[1] = byte(currentTime >> 16);
hello.random[2] = byte(currentTime >> 8);
hello.random[3] = byte(currentTime);
_, err := io.ReadFull(config.Rand, hello.random[4:len(hello.random)]);
if err != nil {
h.error(alertInternalError);
return;
}
hello.compressionMethod = compressionNone;
finishedHash.Write(hello.marshal());
writeChan <- writerSetVersion{major, minor};
writeChan <- hello;
if len(config.Certificates) == 0 {
h.error(alertInternalError);
return;
}
certMsg := new(certificateMsg);
certMsg.certificates = config.Certificates[0].Certificate;
finishedHash.Write(certMsg.marshal());
writeChan <- certMsg;
helloDone := new(serverHelloDoneMsg);
finishedHash.Write(helloDone.marshal());
writeChan <- helloDone;
ckx, ok := h.readHandshakeMsg().(*clientKeyExchangeMsg);
if !ok {
h.error(alertUnexpectedMessage);
return;
}
finishedHash.Write(ckx.marshal());
preMasterSecret := make([]byte, 48);
_, err = io.ReadFull(config.Rand, preMasterSecret[2:len(preMasterSecret)]);
if err != nil {
h.error(alertInternalError);
return;
}
err = rsa.DecryptPKCS1v15SessionKey(config.Rand, config.Certificates[0].PrivateKey, ckx.ciphertext, preMasterSecret);
if err != nil {
h.error(alertHandshakeFailure);
return;
}
// 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 :=
keysFromPreMasterSecret11(preMasterSecret, clientHello.random, hello.random, suite.hashLength, suite.cipherKeyLength);
_, ok = h.readHandshakeMsg().(changeCipherSpec);
if !ok {
h.error(alertUnexpectedMessage);
return;
}
cipher, _ := rc4.NewCipher(clientKey);
controlChan <- &newCipherSpec{cipher, hmac.New(sha1.New(), clientMAC)};
clientFinished, ok := h.readHandshakeMsg().(*finishedMsg);
if !ok {
h.error(alertUnexpectedMessage);
return;
}
verify := finishedHash.clientSum(masterSecret);
if len(verify) != len(clientFinished.verifyData) ||
subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
h.error(alertHandshakeFailure);
return;
}
controlChan <- ConnectionState{true, "TLS_RSA_WITH_RC4_128_SHA", 0};
finishedHash.Write(clientFinished.marshal());
cipher2, _ := rc4.NewCipher(serverKey);
writeChan <- writerChangeCipherSpec{cipher2, hmac.New(sha1.New(), serverMAC)};
finished := new(finishedMsg);
finished.verifyData = finishedHash.serverSum(masterSecret);
writeChan <- finished;
writeChan <- writerEnableApplicationData{};
for {
_, ok := h.readHandshakeMsg().(*clientHelloMsg);
if !ok {
h.error(alertUnexpectedMessage);
return;
}
// We reject all renegotication requests.
writeChan <- alert{alertLevelWarning, alertNoRenegotiation};
}
}
func (h *serverHandshake) readHandshakeMsg() interface{} {
v := <-h.msgChan;
if closed(h.msgChan) {
// If the channel closed then the processor received an error
// from the peer and we don't want to echo it back to them.
h.msgChan = nil;
return 0;
}
if _, ok := v.(alert); ok {
// We got an alert from the processor. We forward to the writer
// and shutdown.
h.writeChan <- v;
h.msgChan = nil;
return 0;
}
return v;
}
func (h *serverHandshake) error(e alertType) {
if h.msgChan != nil {
// If we didn't get an error from the processor, then we need
// to tell it about the error.
h.controlChan <- ConnectionState{false, "", e};
close(h.controlChan);
go func() { for _ = range h.msgChan {} }();
h.writeChan <- alert{alertLevelError, e};
}
}

210
handshake_server_test.go Normal file
View File

@ -0,0 +1,210 @@
// Copyright 2009 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";
"big";
"crypto/rsa";
"os";
"testing";
"testing/script";
)
type zeroSource struct{}
func (zeroSource) Read(b []byte) (n int, err os.Error) {
for i := range b {
b[i] = 0;
}
return len(b), nil;
}
var testConfig *Config
func init() {
testConfig = new(Config);
testConfig.Time = func() int64 { return 0 };
testConfig.Rand = zeroSource{};
testConfig.Certificates = make([]Certificate, 1);
testConfig.Certificates[0].Certificate = [][]byte{testCertificate};
testConfig.Certificates[0].PrivateKey = testPrivateKey;
}
func setupServerHandshake() (writeChan chan interface{}, controlChan chan interface{}, msgChan chan interface{}) {
sh := new(serverHandshake);
writeChan = make(chan interface{});
controlChan = make(chan interface{});
msgChan = make(chan interface{});
go sh.loop(writeChan, controlChan, msgChan, testConfig);
return;
}
func testClientHelloFailure(t *testing.T, clientHello interface{}, expectedAlert alertType) {
writeChan, controlChan, msgChan := setupServerHandshake();
defer close(msgChan);
send := script.NewEvent("send", nil, script.Send{msgChan, clientHello});
recvAlert := script.NewEvent("recv alert", []*script.Event{send}, script.Recv{writeChan, alert{alertLevelError, expectedAlert}});
close1 := script.NewEvent("msgChan close", []*script.Event{recvAlert}, script.Closed{writeChan});
recvState := script.NewEvent("recv state", []*script.Event{send}, script.Recv{controlChan, ConnectionState{false, "", expectedAlert}});
close2 := script.NewEvent("controlChan close", []*script.Event{recvState}, script.Closed{controlChan});
err := script.Perform(0, []*script.Event{send, recvAlert, close1, recvState, close2});
if err != nil {
t.Errorf("Got error: %s", err);
}
}
func TestSimpleError(t *testing.T) {
testClientHelloFailure(t, &serverHelloDoneMsg{}, alertUnexpectedMessage);
}
var badProtocolVersions = []uint8{0, 0, 0, 5, 1, 0, 1, 5, 2, 0, 2, 5, 3, 0}
func TestRejectBadProtocolVersion(t *testing.T) {
clientHello := new(clientHelloMsg);
for i := 0; i < len(badProtocolVersions); i += 2 {
clientHello.major = badProtocolVersions[i];
clientHello.minor = badProtocolVersions[i+1];
testClientHelloFailure(t, clientHello, alertProtocolVersion);
}
}
func TestNoSuiteOverlap(t *testing.T) {
clientHello := &clientHelloMsg{nil, 3, 1, nil, nil, []uint16{0xff00}, []uint8{0}};
testClientHelloFailure(t, clientHello, alertHandshakeFailure);
}
func TestNoCompressionOverlap(t *testing.T) {
clientHello := &clientHelloMsg{nil, 3, 1, nil, nil, []uint16{TLS_RSA_WITH_RC4_128_SHA}, []uint8{0xff}};
testClientHelloFailure(t, clientHello, alertHandshakeFailure);
}
func matchServerHello(v interface{}) bool {
serverHello, ok := v.(*serverHelloMsg);
if !ok {
return false;
}
return serverHello.major == 3 &&
serverHello.minor == 2 &&
serverHello.cipherSuite == TLS_RSA_WITH_RC4_128_SHA &&
serverHello.compressionMethod == compressionNone;
}
func TestAlertForwarding(t *testing.T) {
writeChan, controlChan, msgChan := setupServerHandshake();
defer close(msgChan);
a := alert{alertLevelError, alertNoRenegotiation};
sendAlert := script.NewEvent("send alert", nil, script.Send{msgChan, a});
recvAlert := script.NewEvent("recv alert", []*script.Event{sendAlert}, script.Recv{writeChan, a});
closeWriter := script.NewEvent("close writer", []*script.Event{recvAlert}, script.Closed{writeChan});
closeControl := script.NewEvent("close control", []*script.Event{recvAlert}, script.Closed{controlChan});
err := script.Perform(0, []*script.Event{sendAlert, recvAlert, closeWriter, closeControl});
if err != nil {
t.Errorf("Got error: %s", err);
}
}
func TestClose(t *testing.T) {
writeChan, controlChan, msgChan := setupServerHandshake();
close := script.NewEvent("close", nil, script.Close{msgChan});
closed1 := script.NewEvent("closed1", []*script.Event{close}, script.Closed{writeChan});
closed2 := script.NewEvent("closed2", []*script.Event{close}, script.Closed{controlChan});
err := script.Perform(0, []*script.Event{close, closed1, closed2});
if err != nil {
t.Errorf("Got error: %s", err);
}
}
func matchCertificate(v interface{}) bool {
cert, ok := v.(*certificateMsg);
if !ok {
return false;
}
return len(cert.certificates) == 1 &&
bytes.Compare(cert.certificates[0], testCertificate) == 0;
}
func matchSetCipher(v interface{}) bool {
_, ok := v.(writerChangeCipherSpec);
return ok;
}
func matchDone(v interface{}) bool {
_, ok := v.(*serverHelloDoneMsg);
return ok;
}
func matchFinished(v interface{}) bool {
finished, ok := v.(*finishedMsg);
if !ok {
return false;
}
return bytes.Compare(finished.verifyData, fromHex("29122ae11453e631487b02ed")) == 0;
}
func matchNewCipherSpec(v interface{}) bool {
_, ok := v.(*newCipherSpec);
return ok;
}
func TestFullHandshake(t *testing.T) {
writeChan, controlChan, msgChan := setupServerHandshake();
defer close(msgChan);
// The values for this test were obtained from running `gnutls-cli --insecure --debug 9`
clientHello := &clientHelloMsg{fromHex("0100007603024aef7d77e4686d5dfd9d953dfe280788759ffd440867d687670216da45516b310000340033004500390088001600320044003800870013006600900091008f008e002f004100350084000a00050004008c008d008b008a01000019000900030200010000000e000c0000093132372e302e302e31"), 3, 2, fromHex("4aef7d77e4686d5dfd9d953dfe280788759ffd440867d687670216da45516b31"), nil, []uint16{0x33, 0x45, 0x39, 0x88, 0x16, 0x32, 0x44, 0x38, 0x87, 0x13, 0x66, 0x90, 0x91, 0x8f, 0x8e, 0x2f, 0x41, 0x35, 0x84, 0xa, 0x5, 0x4, 0x8c, 0x8d, 0x8b, 0x8a}, []uint8{0x0}};
sendHello := script.NewEvent("send hello", nil, script.Send{msgChan, clientHello});
setVersion := script.NewEvent("set version", []*script.Event{sendHello}, script.Recv{writeChan, writerSetVersion{3, 2}});
recvHello := script.NewEvent("recv hello", []*script.Event{setVersion}, script.RecvMatch{writeChan, matchServerHello});
recvCert := script.NewEvent("recv cert", []*script.Event{recvHello}, script.RecvMatch{writeChan, matchCertificate});
recvDone := script.NewEvent("recv done", []*script.Event{recvCert}, script.RecvMatch{writeChan, matchDone});
ckx := &clientKeyExchangeMsg{nil, fromHex("872e1fee5f37dd86f3215938ac8de20b302b90074e9fb93097e6b7d1286d0f45abf2daf179deb618bb3c70ed0afee6ee24476ee4649e5a23358143c0f1d9c251")};
sendCKX := script.NewEvent("send ckx", []*script.Event{recvDone}, script.Send{msgChan, ckx});
sendCCS := script.NewEvent("send ccs", []*script.Event{sendCKX}, script.Send{msgChan, changeCipherSpec{}});
recvNCS := script.NewEvent("recv done", []*script.Event{sendCCS}, script.RecvMatch{controlChan, matchNewCipherSpec});
finished := &finishedMsg{nil, fromHex("c8faca5d242f4423325c5b1a")};
sendFinished := script.NewEvent("send finished", []*script.Event{recvNCS}, script.Send{msgChan, finished});
recvFinished := script.NewEvent("recv finished", []*script.Event{sendFinished}, script.RecvMatch{writeChan, matchFinished});
setCipher := script.NewEvent("set cipher", []*script.Event{sendFinished}, script.RecvMatch{writeChan, matchSetCipher});
recvConnectionState := script.NewEvent("recv state", []*script.Event{sendFinished}, script.Recv{controlChan, ConnectionState{true, "TLS_RSA_WITH_RC4_128_SHA", 0}});
err := script.Perform(0, []*script.Event{sendHello, setVersion, recvHello, recvCert, recvDone, sendCKX, sendCCS, recvNCS, sendFinished, setCipher, recvConnectionState, recvFinished});
if err != nil {
t.Errorf("Got error: %s", err);
}
}
var testCertificate = fromHex("3082025930820203a003020102020900c2ec326b95228959300d06092a864886f70d01010505003054310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464310d300b0603550403130474657374301e170d3039313032303232323434355a170d3130313032303232323434355a3054310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464310d300b0603550403130474657374305c300d06092a864886f70d0101010500034b003048024100b2990f49c47dfa8cd400ae6a4d1b8a3b6a13642b23f28b003bfb97790ade9a4cc82b8b2a81747ddec08b6296e53a08c331687ef25c4bf4936ba1c0e6041e9d150203010001a381b73081b4301d0603551d0e0416041478a06086837c9293a8c9b70c0bdabdb9d77eeedf3081840603551d23047d307b801478a06086837c9293a8c9b70c0bdabdb9d77eeedfa158a4563054310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464310d300b0603550403130474657374820900c2ec326b95228959300c0603551d13040530030101ff300d06092a864886f70d0101050500034100ac23761ae1349d85a439caad4d0b932b09ea96de1917c3e0507c446f4838cb3076fb4d431db8c1987e96f1d7a8a2054dea3a64ec99a3f0eda4d47a163bf1f6ac")
func bigFromString(s string) *big.Int {
ret := new(big.Int);
ret.SetString(s, 10);
return ret;
}
var testPrivateKey = &rsa.PrivateKey{
PublicKey: rsa.PublicKey{
N: bigFromString("9353930466774385905609975137998169297361893554149986716853295022578535724979677252958524466350471210367835187480748268864277464700638583474144061408845077"),
E: 65537,
},
D: bigFromString("7266398431328116344057699379749222532279343923819063639497049039389899328538543087657733766554155839834519529439851673014800261285757759040931985506583861"),
P: bigFromString("98920366548084643601728869055592650835572950932266967461790948584315647051443"),
Q: bigFromString("94560208308847015747498523884063394671606671904944666360068158221458669711639"),
}

292
record_process.go Normal file
View File

@ -0,0 +1,292 @@
// Copyright 2009 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
// A recordProcessor accepts reassembled records, decrypts and verifies them
// and routes them either to the handshake processor, to up to the application.
// It also accepts requests from the application for the current connection
// state, or for a notification when the state changes.
import (
"bytes";
"container/list";
"crypto/subtle";
"hash";
)
// getConnectionState is a request from the application to get the current
// ConnectionState.
type getConnectionState struct {
reply chan<- ConnectionState;
}
// waitConnectionState is a request from the application to be notified when
// the connection state changes.
type waitConnectionState struct {
reply chan<- ConnectionState;
}
// connectionStateChange is a message from the handshake processor that the
// connection state has changed.
type connectionStateChange struct {
connState ConnectionState;
}
// changeCipherSpec is a message send to the handshake processor to signal that
// the peer is switching ciphers.
type changeCipherSpec struct{}
// newCipherSpec is a message from the handshake processor that future
// records should be processed with a new cipher and MAC function.
type newCipherSpec struct {
encrypt encryptor;
mac hash.Hash;
}
type recordProcessor struct {
decrypt encryptor;
mac hash.Hash;
seqNum uint64;
handshakeBuf []byte;
appDataChan chan<- []byte;
requestChan <-chan interface{};
controlChan <-chan interface{};
recordChan <-chan *record;
handshakeChan chan<- interface{};
// recordRead is nil when we don't wish to read any more.
recordRead <-chan *record;
// appDataSend is nil when len(appData) == 0.
appDataSend chan<- []byte;
// appData contains any application data queued for upstream.
appData []byte;
// A list of channels waiting for connState to change.
waitQueue *list.List;
connState ConnectionState;
shutdown bool;
header [13]byte;
}
// drainRequestChannel processes messages from the request channel until it's closed.
func drainRequestChannel(requestChan <-chan interface{}, c ConnectionState) {
for v := range requestChan {
if closed(requestChan) {
return;
}
switch r := v.(type) {
case getConnectionState:
r.reply <- c;
case waitConnectionState:
r.reply <- c;
}
}
}
func (p *recordProcessor) loop(appDataChan chan<- []byte, requestChan <-chan interface{}, controlChan <-chan interface{}, recordChan <-chan *record, handshakeChan chan<- interface{}) {
noop := nop{};
p.decrypt = noop;
p.mac = noop;
p.waitQueue = list.New();
p.appDataChan = appDataChan;
p.requestChan = requestChan;
p.controlChan = controlChan;
p.recordChan = recordChan;
p.handshakeChan = handshakeChan;
p.recordRead = recordChan;
for !p.shutdown {
select {
case p.appDataSend <- p.appData:
p.appData = nil;
p.appDataSend = nil;
p.recordRead = p.recordChan;
case c := <-controlChan:
p.processControlMsg(c);
case r := <-requestChan:
p.processRequestMsg(r);
case r := <-p.recordRead:
p.processRecord(r);
}
}
p.wakeWaiters();
go drainRequestChannel(p.requestChan, p.connState);
go func() { for _ = range controlChan {} }();
close(handshakeChan);
if len(p.appData) > 0 {
appDataChan <- p.appData;
}
close(appDataChan);
}
func (p *recordProcessor) processRequestMsg(requestMsg interface{}) {
if closed(p.requestChan) {
p.shutdown = true;
return;
}
switch r := requestMsg.(type) {
case getConnectionState:
r.reply <- p.connState;
case waitConnectionState:
if p.connState.HandshakeComplete {
r.reply <- p.connState;
}
p.waitQueue.PushBack(r.reply);
}
}
func (p *recordProcessor) processControlMsg(msg interface{}) {
connState, ok := msg.(ConnectionState);
if !ok || closed(p.controlChan) {
p.shutdown = true;
return;
}
p.connState = connState;
p.wakeWaiters();
}
func (p *recordProcessor) wakeWaiters() {
for i := p.waitQueue.Front(); i != nil; i = i.Next() {
i.Value.(chan<- ConnectionState) <- p.connState;
}
p.waitQueue.Init();
}
func (p *recordProcessor) processRecord(r *record) {
if closed(p.recordChan) {
p.shutdown = true;
return;
}
p.decrypt.XORKeyStream(r.payload);
if len(r.payload) < p.mac.Size() {
p.error(alertBadRecordMAC);
return;
}
fillMACHeader(&p.header, p.seqNum, len(r.payload) - p.mac.Size(), r);
p.seqNum++;
p.mac.Reset();
p.mac.Write(p.header[0:13]);
p.mac.Write(r.payload[0 : len(r.payload) - p.mac.Size()]);
macBytes := p.mac.Sum();
if subtle.ConstantTimeCompare(macBytes, r.payload[len(r.payload) - p.mac.Size() : len(r.payload)]) != 1 {
p.error(alertBadRecordMAC);
return;
}
switch r.contentType {
case recordTypeHandshake:
p.processHandshakeRecord(r.payload[0 : len(r.payload) - p.mac.Size()]);
case recordTypeChangeCipherSpec:
if len(r.payload) != 1 || r.payload[0] != 1 {
p.error(alertUnexpectedMessage);
return;
}
p.handshakeChan <- changeCipherSpec{};
newSpec, ok := (<-p.controlChan).(*newCipherSpec);
if !ok {
p.connState.Error = alertUnexpectedMessage;
p.shutdown = true;
return;
}
p.decrypt = newSpec.encrypt;
p.mac = newSpec.mac;
p.seqNum = 0;
case recordTypeApplicationData:
if p.connState.HandshakeComplete == false {
p.error(alertUnexpectedMessage);
return;
}
p.recordRead = nil;
p.appData = r.payload;
p.appDataSend = p.appDataChan;
default:
p.error(alertUnexpectedMessage);
return;
}
}
func (p *recordProcessor) processHandshakeRecord(data []byte) {
if p.handshakeBuf == nil {
p.handshakeBuf = data;
} else {
if len(p.handshakeBuf) > maxHandshakeMsg {
p.error(alertInternalError);
return;
}
newBuf := make([]byte, len(p.handshakeBuf)+len(data));
bytes.Copy(newBuf, p.handshakeBuf);
bytes.Copy(newBuf[len(p.handshakeBuf):len(newBuf)], data);
p.handshakeBuf = newBuf;
}
for len(p.handshakeBuf) >= 4 {
handshakeLen := int(p.handshakeBuf[1])<<16 |
int(p.handshakeBuf[2])<<8 |
int(p.handshakeBuf[3]);
if handshakeLen + 4 > len(p.handshakeBuf) {
break;
}
bytes := p.handshakeBuf[0 : handshakeLen + 4];
p.handshakeBuf = p.handshakeBuf[handshakeLen + 4 : len(p.handshakeBuf)];
if bytes[0] == typeFinished {
// Special case because Finished is synchronous: the
// handshake handler has to tell us if it's ok to start
// forwarding application data.
m := new(finishedMsg);
if !m.unmarshal(bytes) {
p.error(alertUnexpectedMessage);
}
p.handshakeChan <- m;
var ok bool;
p.connState, ok = (<-p.controlChan).(ConnectionState);
if !ok || p.connState.Error != 0 {
p.shutdown = true;
return;
}
} else {
msg, ok := parseHandshakeMsg(bytes);
if !ok {
p.error(alertUnexpectedMessage);
return;
}
p.handshakeChan <- msg;
}
}
}
func (p *recordProcessor) error(err alertType) {
close(p.handshakeChan);
p.connState.Error = err;
p.wakeWaiters();
p.shutdown = true;
}
func parseHandshakeMsg(data []byte) (interface{}, bool) {
var m interface {
unmarshal([]byte) bool;
}
switch data[0] {
case typeClientHello:
m = new(clientHelloMsg);
case typeClientKeyExchange:
m = new(clientKeyExchangeMsg);
default:
return nil, false;
}
ok := m.unmarshal(data);
return m, ok;
}

137
record_process_test.go Normal file
View File

@ -0,0 +1,137 @@
// Copyright 2009 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 (
"encoding/hex";
"testing";
"testing/script";
)
func setup() (appDataChan chan []byte, requestChan chan interface{}, controlChan chan interface{}, recordChan chan *record, handshakeChan chan interface{}) {
rp := new(recordProcessor);
appDataChan = make(chan []byte);
requestChan = make(chan interface{});
controlChan = make(chan interface{});
recordChan = make(chan *record);
handshakeChan = make(chan interface{});
go rp.loop(appDataChan, requestChan, controlChan, recordChan, handshakeChan);
return;
}
func fromHex(s string) []byte {
b, _ := hex.DecodeString(s);
return b;
}
func TestNullConnectionState(t *testing.T) {
_, requestChan, controlChan, recordChan, _ := setup();
defer close(requestChan);
defer close(controlChan);
defer close(recordChan);
// Test a simple request for the connection state.
replyChan := make(chan ConnectionState);
sendReq := script.NewEvent("send request", nil, script.Send{requestChan, getConnectionState{replyChan}});
getReply := script.NewEvent("get reply", []*script.Event{sendReq}, script.Recv{replyChan, ConnectionState{false, "", 0}});
err := script.Perform(0, []*script.Event{sendReq, getReply});
if err != nil {
t.Errorf("Got error: %s", err);
}
}
func TestWaitConnectionState(t *testing.T) {
_, requestChan, controlChan, recordChan, _ := setup();
defer close(requestChan);
defer close(controlChan);
defer close(recordChan);
// Test that waitConnectionState doesn't get a reply until the connection state changes.
replyChan := make(chan ConnectionState);
sendReq := script.NewEvent("send request", nil, script.Send{requestChan, waitConnectionState{replyChan}});
replyChan2 := make(chan ConnectionState);
sendReq2 := script.NewEvent("send request 2", []*script.Event{sendReq}, script.Send{requestChan, getConnectionState{replyChan2}});
getReply2 := script.NewEvent("get reply 2", []*script.Event{sendReq2}, script.Recv{replyChan2, ConnectionState{false, "", 0}});
sendState := script.NewEvent("send state", []*script.Event{getReply2}, script.Send{controlChan, ConnectionState{true, "test", 1}});
getReply := script.NewEvent("get reply", []*script.Event{sendState}, script.Recv{replyChan, ConnectionState{true, "test", 1}});
err := script.Perform(0, []*script.Event{sendReq, sendReq2, getReply2, sendState, getReply});
if err != nil {
t.Errorf("Got error: %s", err);
}
}
func TestHandshakeAssembly(t *testing.T) {
_, requestChan, controlChan, recordChan, handshakeChan := setup();
defer close(requestChan);
defer close(controlChan);
defer close(recordChan);
// Test the reassembly of a fragmented handshake message.
send1 := script.NewEvent("send 1", nil, script.Send{recordChan, &record{recordTypeHandshake, 0, 0, fromHex("10000003")}});
send2 := script.NewEvent("send 2", []*script.Event{send1}, script.Send{recordChan, &record{recordTypeHandshake, 0, 0, fromHex("0001")}});
send3 := script.NewEvent("send 3", []*script.Event{send2}, script.Send{recordChan, &record{recordTypeHandshake, 0, 0, fromHex("42")}});
recvMsg := script.NewEvent("recv", []*script.Event{send3}, script.Recv{handshakeChan, &clientKeyExchangeMsg{fromHex("10000003000142"), fromHex("42")}});
err := script.Perform(0, []*script.Event{send1, send2, send3, recvMsg});
if err != nil {
t.Errorf("Got error: %s", err);
}
}
func TestEarlyApplicationData(t *testing.T) {
_, requestChan, controlChan, recordChan, handshakeChan := setup();
defer close(requestChan);
defer close(controlChan);
defer close(recordChan);
// Test that applicaton data received before the handshake has completed results in an error.
send := script.NewEvent("send", nil, script.Send{recordChan, &record{recordTypeApplicationData, 0, 0, fromHex("")}});
recv := script.NewEvent("recv", []*script.Event{send}, script.Closed{handshakeChan});
err := script.Perform(0, []*script.Event{send, recv});
if err != nil {
t.Errorf("Got error: %s", err);
}
}
func TestApplicationData(t *testing.T) {
appDataChan, requestChan, controlChan, recordChan, handshakeChan := setup();
defer close(requestChan);
defer close(controlChan);
defer close(recordChan);
// Test that the application data is forwarded after a successful Finished message.
send1 := script.NewEvent("send 1", nil, script.Send{recordChan, &record{recordTypeHandshake, 0, 0, fromHex("1400000c000000000000000000000000")}});
recv1 := script.NewEvent("recv finished", []*script.Event{send1}, script.Recv{handshakeChan, &finishedMsg{fromHex("1400000c000000000000000000000000"), fromHex("000000000000000000000000")}});
send2 := script.NewEvent("send connState", []*script.Event{recv1}, script.Send{controlChan, ConnectionState{true, "", 0}});
send3 := script.NewEvent("send 2", []*script.Event{send2}, script.Send{recordChan, &record{recordTypeApplicationData, 0, 0, fromHex("0102")}});
recv2 := script.NewEvent("recv data", []*script.Event{send3}, script.Recv{appDataChan, []byte{0x01, 0x02}});
err := script.Perform(0, []*script.Event{send1, recv1, send2, send3, recv2});
if err != nil {
t.Errorf("Got error: %s", err);
}
}
func TestInvalidChangeCipherSpec(t *testing.T) {
appDataChan, requestChan, controlChan, recordChan, handshakeChan := setup();
defer close(requestChan);
defer close(controlChan);
defer close(recordChan);
send1 := script.NewEvent("send 1", nil, script.Send{recordChan, &record{recordTypeChangeCipherSpec, 0, 0, []byte{1}}});
recv1 := script.NewEvent("recv 1", []*script.Event{send1}, script.Recv{handshakeChan, changeCipherSpec{}});
send2 := script.NewEvent("send 2", []*script.Event{recv1}, script.Send{controlChan, ConnectionState{false, "", 42}});
close := script.NewEvent("close 1", []*script.Event{send2}, script.Closed{appDataChan});
close2 := script.NewEvent("close 2", []*script.Event{send2}, script.Closed{handshakeChan});
err := script.Perform(0, []*script.Event{send1, recv1, send2, close, close2});
if err != nil {
t.Errorf("Got error: %s", err);
}
}