213 lines
5.2 KiB
Go
213 lines
5.2 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() {
|
||
|
header, err := shared.ReceiveBytes(p.port, 1, waitForInitialByte)
|
||
|
if err != nil {
|
||
|
p.errors <- err
|
||
|
return
|
||
|
}
|
||
|
if header[0] != shared.HandshakeByte {
|
||
|
p.errors <- fmt.Errorf(
|
||
|
"unexpected byte 0x%x expected 0x%x",
|
||
|
header[0],
|
||
|
shared.HandshakeByte,
|
||
|
)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
tries := maxTriesPerBlock
|
||
|
|
||
|
var command *datastructures.SignerRequest
|
||
|
for {
|
||
|
if tries <= 0 {
|
||
|
p.errors <- errors.New("tried reading block too often")
|
||
|
break
|
||
|
}
|
||
|
tries--
|
||
|
if err := shared.SendBytes(p.port, []byte{shared.AckByte}); err != nil {
|
||
|
p.errors <- fmt.Errorf("could not write ACK byte: %v", err)
|
||
|
break
|
||
|
}
|
||
|
|
||
|
lengthBytes, err := shared.ReceiveBytes(p.port, shared.LengthFieldSize, waitForLengthBytes)
|
||
|
if err != nil {
|
||
|
p.errors <- fmt.Errorf("could not read lenght bytes: %v", err)
|
||
|
break
|
||
|
}
|
||
|
blockLength := datastructures.Decode24BitLength(lengthBytes)
|
||
|
blockData, err := shared.ReceiveBytes(p.port, blockLength, waitForBlock)
|
||
|
if err != nil {
|
||
|
p.errors <- fmt.Errorf("could not read data block: %v", err)
|
||
|
if !p.requestResend() {
|
||
|
break
|
||
|
}
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
checkSum, err := shared.ReceiveBytes(p.port, shared.CheckSumFieldSize, waitForChecksumByte)
|
||
|
if err != nil {
|
||
|
p.errors <- fmt.Errorf("could not read checksum byte: %v", err)
|
||
|
break
|
||
|
}
|
||
|
|
||
|
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() {
|
||
|
break
|
||
|
}
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
trailer, err := shared.ReceiveBytes(p.port, shared.TrailerFieldSize, waitForTrailerBytes)
|
||
|
if err != nil {
|
||
|
p.errors <- fmt.Errorf("could not read trailer bytes: %v", err)
|
||
|
break
|
||
|
}
|
||
|
|
||
|
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: %v", err)
|
||
|
break
|
||
|
}
|
||
|
|
||
|
if err := shared.SendBytes(p.port, []byte{shared.AckByte}); err != nil {
|
||
|
p.errors <- fmt.Errorf("failed to send ACK byte: %v", err)
|
||
|
break
|
||
|
}
|
||
|
|
||
|
p.commandChan <- command
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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 err
|
||
|
}
|
||
|
|
||
|
if ack, err := shared.ReceiveBytes(p.port, 1, waitForHandShake); err != nil {
|
||
|
return err
|
||
|
} else if ack[0] != shared.AckByte {
|
||
|
return errors.New(fmt.Sprintf("invalid ack byte 0x%02x", ack[0]))
|
||
|
}
|
||
|
|
||
|
tryAgain := true
|
||
|
for tryAgain {
|
||
|
data := response.Serialize()
|
||
|
if err := shared.SendBytes(p.port, data); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
checksum := datastructures.CalculateXorCheckSum([][]byte{data})
|
||
|
if err := shared.SendBytes(p.port, []byte{checksum}); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if err := shared.SendBytes(p.port, []byte(shared.MagicTrailer)); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if ack, err := shared.ReceiveBytes(p.port, 1, waitForHandShake); err != nil {
|
||
|
return err
|
||
|
} else if ack[0] == shared.AckByte {
|
||
|
tryAgain = false
|
||
|
} else if ack[0] != 0x11 {
|
||
|
return errors.New(fmt.Sprintf("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: &CommandProcessor{},
|
||
|
}
|
||
|
}
|