// 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 ( "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():]) != 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[0 : len(r.payload)-p.mac.Size()] 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)) copy(newBuf, p.handshakeBuf) copy(newBuf[len(p.handshakeBuf):], 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:] 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 typeServerHello: m = new(serverHelloMsg) case typeCertificate: m = new(certificateMsg) case typeServerHelloDone: m = new(serverHelloDoneMsg) case typeClientKeyExchange: m = new(clientKeyExchangeMsg) default: return nil, false } ok := m.unmarshal(data) return m, ok }