cacert-gosigner/signer/port_handler.go

241 lines
5.6 KiB
Go

package signer
import (
"errors"
"fmt"
"io"
"time"
log "github.com/sirupsen/logrus"
"git.cacert.org/cacert-gosigner/datastructures"
"git.cacert.org/cacert-gosigner/shared"
)
const (
waitForHandShake = time.Second * 5
waitForInitialByte = time.Second * 20
waitForLengthBytes = time.Second * 2
waitForChecksumByte = time.Second * 2
waitForTrailerBytes = time.Second * 2
waitForBlock = time.Minute * 1
maxTriesPerBlock = 10_000
)
// The PortHandler takes care of reading and writing of raw bytes from/to a serial port
type PortHandler struct {
port io.ReadWriteCloser
processor *CommandProcessor
errors chan error
commandChan chan *datastructures.SignerRequest
}
func (p *PortHandler) MainLoop() {
count := 0
for {
go p.Receive()
select {
case command := <-p.commandChan:
if command == nil {
log.Infof("command channel has been closed. stopping execution.")
return
}
log.Debugf("received command %v", command)
response, err := p.processor.Process(command)
if err != nil {
log.Errorf("error processing command: %v", err)
close(p.commandChan)
} else {
_ = p.SendResponse(response)
}
case err := <-p.errors:
log.Errorf("error from io goroutine %v", err)
}
count++
log.Infof("%d requests processed. Waiting for next request ...", count)
}
}
// Receive a request and generate a request data structure
func (p *PortHandler) Receive() {
if err := p.receiveHandshake(); err != nil {
p.errors <- err
return
}
var command *datastructures.SignerRequest
for tries := maxTriesPerBlock; tries > 0; tries-- {
if err := shared.SendBytes(p.port, []byte{shared.AckByte}); err != nil {
p.errors <- fmt.Errorf("could not write ACK byte: %w", err)
return
}
lengthBytes, err := shared.ReceiveBytes(p.port, shared.LengthFieldSize, waitForLengthBytes)
if err != nil {
p.errors <- fmt.Errorf("could not read length bytes: %w", err)
return
}
blockLength := datastructures.Decode24BitLength(lengthBytes)
blockData, err := shared.ReceiveBytes(p.port, blockLength, waitForBlock)
if err != nil {
p.errors <- fmt.Errorf("could not read data block: %w", err)
if !p.requestResend() {
return
}
continue
}
checkSum, err := shared.ReceiveBytes(p.port, shared.CheckSumFieldSize, waitForChecksumByte)
if err != nil {
p.errors <- fmt.Errorf("could not read checksum byte: %w", err)
return
}
calculated := datastructures.CalculateXorCheckSum([][]byte{lengthBytes, blockData})
if checkSum[0] != calculated {
p.errors <- fmt.Errorf("CRC error. expected 0x%02x, got 0x%02x", calculated, checkSum[0])
if !p.requestResend() {
return
}
continue
}
trailer, err := shared.ReceiveBytes(p.port, shared.TrailerFieldSize, waitForTrailerBytes)
if err != nil {
p.errors <- fmt.Errorf("could not read trailer bytes: %w", err)
return
}
if string(trailer) != shared.MagicTrailer {
p.errors <- fmt.Errorf(
"BROKEN block detected, expected trailer bytes '%s' not found, got '%s' instead",
shared.MagicTrailer,
trailer,
)
if !p.requestResend() {
break
}
continue
}
command, err = datastructures.SignerRequestFromData(blockData)
if err != nil {
p.errors <- fmt.Errorf("failed to parse block as signer request: %w", err)
return
}
if err := shared.SendBytes(p.port, []byte{shared.AckByte}); err != nil {
p.errors <- fmt.Errorf("failed to send ACK byte: %w", err)
return
}
p.commandChan <- command
return
}
p.errors <- errors.New("tried reading block too often")
}
func (p *PortHandler) receiveHandshake() error {
header, err := shared.ReceiveBytes(p.port, 1, waitForInitialByte)
if err != nil {
return fmt.Errorf("could not receive initial byte: %w", err)
}
if header[0] != shared.HandshakeByte {
return fmt.Errorf(
"unexpected byte 0x%x expected 0x%x",
header[0],
shared.HandshakeByte,
)
}
return nil
}
func (p *PortHandler) requestResend() bool {
if err := shared.SendBytes(p.port, []byte{shared.ResendByte}); err != nil {
p.errors <- err
return false
}
return true
}
// Send a response to the client
func (p *PortHandler) SendResponse(response *datastructures.SignerResponse) error {
if err := shared.SendBytes(p.port, []byte{shared.HandshakeByte}); err != nil {
return fmt.Errorf("could not send handshake: %w", err)
}
if ack, err := shared.ReceiveBytes(p.port, 1, waitForHandShake); err != nil {
return fmt.Errorf("could not receive ACK: %w", err)
} else if ack[0] != shared.AckByte {
return fmt.Errorf("invalid ack byte 0x%02x", ack[0])
}
tryAgain := true
for tryAgain {
data := response.Serialize()
if err := shared.SendBytes(p.port, data); err != nil {
return fmt.Errorf("could not send data block: %w", err)
}
checksum := datastructures.CalculateXorCheckSum([][]byte{data})
if err := shared.SendBytes(p.port, []byte{checksum}); err != nil {
return fmt.Errorf("could not send checksum: %w", err)
}
if err := shared.SendBytes(p.port, []byte(shared.MagicTrailer)); err != nil {
return fmt.Errorf("could not send trailer: %w", err)
}
ack, err := shared.ReceiveBytes(p.port, 1, waitForHandShake)
if err != nil {
return fmt.Errorf("could not receive ACK: %w", err)
}
if ack[0] == shared.AckByte {
tryAgain = false
} else {
return fmt.Errorf("invalid ack byte 0x%02x", ack[0])
}
}
return nil
}
func NewSignerProcess(port io.ReadWriteCloser) *PortHandler {
errorChan := make(chan error)
return &PortHandler{
port: port,
errors: errorChan,
commandChan: make(chan *datastructures.SignerRequest, 1),
processor: NewCommandProcessor(),
}
}