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(), } }