Plan 9 from Bell Labs’s /usr/web/sources/contrib/ericvh/go-plan9/src/pkg/crypto/tls/record_process.go

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


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

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.