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{}, } }