--- /dev/null
+// 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};
+ }
+}
--- /dev/null
+// 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"),
+}
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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);
+ }
+}