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