// 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; }