package main import ( "errors" "flag" "fmt" "io" "time" log "github.com/sirupsen/logrus" "go.bug.st/serial" "git.cacert.org/cacert-gosigner/datastructures" "git.cacert.org/cacert-gosigner/shared" ) var ( address string baudrate int ) func main() { flag.StringVar(&address, "a", "/dev/ttyUSB0", "address") flag.IntVar(&baudrate, "b", 115200, "baud rate") flag.Parse() log.SetFormatter(&log.TextFormatter{ DisableColors: true, FullTimestamp: true, }) serialMode := &serial.Mode{ BaudRate: baudrate, DataBits: 8, StopBits: serial.OneStopBit, Parity: serial.NoParity, } log.Infof("connecting to %s using %+v", address, serialMode) port, err := serial.Open(address, serialMode) if err != nil { log.Fatalf("could not open serial port: %v", err) } log.Info("connected") count := 0 defer func() { err := port.Close() if err != nil { log.Fatalf("could not close port: %v", err) } log.Info("closed") }() readLoop: for { // timeout if no command has been received within 15 seconds timeout := time.After(15 * time.Second) commandChan := make(chan datastructures.SignerRequest, 1) errChan := make(chan error, 1) readWriteCloser := (io.ReadWriteCloser)(port) go Receive(&readWriteCloser, &commandChan, &errChan) select { case command := <-commandChan: response, err := Process(command) if err != nil { log.Errorf("error processing command: %v", err) break readLoop } _ = SendResponse(&readWriteCloser, response) case <-timeout: log.Error("timeout in main loop") break readLoop case err := <-errChan: log.Errorf("error from io goroutine %v", err) } count++ log.Infof("%d requests processed. Waiting for next request ...", count) } } // Send a response to the client func SendResponse(port *io.ReadWriteCloser, response *datastructures.SignerResponse) error { if _, err := (*port).Write([]byte{0x02}); err != nil { return err } if ack, err := shared.ReceiveBytes(port, 1, 5*time.Second); err != nil { return err } else if ack[0] != 0x10 { return errors.New(fmt.Sprintf("invalid ack byte 0x%02x", ack[0])) } tryAgain := true for tryAgain { data := response.Serialize() if _, err := (*port).Write(data); err != nil { return err } checksum := datastructures.CalculateXorCheckSum([][]byte{data}) if _, err := (*port).Write([]byte{checksum}); err != nil { return err } if _, err := (*port).Write([]byte(shared.MagicTrailer)); err != nil { return err } if ack, err := shared.ReceiveBytes(port, 1, 5*time.Second); err != nil { return err } else if ack[0] == 0x10 { tryAgain = false } else if ack[0] != 0x11 { return errors.New(fmt.Sprintf("invalid ack byte 0x%02x", ack[0])) } } return nil } // Process the signer request func Process(command datastructures.SignerRequest) (response *datastructures.SignerResponse, err error) { log.Infof("analyze %+v", command) switch command.Action { case datastructures.ActionNul: response, err = handleNulAction(command) return default: return nil, errors.New(fmt.Sprintf( "unsupported Action 0x%02x %s", int(command.Action), command.Action, )) } } func handleNulAction(command datastructures.SignerRequest) (*datastructures.SignerResponse, error) { // todo: generate script to set system time from command time data in command.content1 return &datastructures.SignerResponse{ Version: command.Version, Action: command.Action, Content1: "", Content2: "", Content3: ""}, nil } // Receive a request and generate a request data structure func Receive(port *io.ReadWriteCloser, commandChan *chan datastructures.SignerRequest, errorChan *chan error) { header, err := shared.ReceiveBytes(port, 1, 20*time.Second) if err != nil { *errorChan <- err return } if header[0] != shared.HandshakeByte { *errorChan <- fmt.Errorf("unexpected byte 0x%x expected 0x%x", header[0], shared.HandshakeByte) } if _, err := (*port).Write([]byte{shared.AckByte}); err != nil { *errorChan <- errors.New("could not write ACK") return } lengthBytes, err := shared.ReceiveBytes(port, 3, 2*time.Second) if err != nil { *errorChan <- err return } blockLength := datastructures.Decode24BitLength(lengthBytes) blockData, err := shared.ReceiveBytes(port, blockLength, 5*time.Second) if err != nil { *errorChan <- err return } checkSum, err := shared.ReceiveBytes(port, 1, 2*time.Second) if err != nil { *errorChan <- err return } command, err := datastructures.SignerRequestFromData(lengthBytes, blockData, checkSum[0]) if err != nil { *errorChan <- err return } trailer, err := shared.ReceiveBytes(port, len(shared.MagicTrailer), 2*time.Second) if err != nil { *errorChan <- err return } if string(trailer) != shared.MagicTrailer { *errorChan <- errors.New("expected trailer bytes not found") return } if _, err := (*port).Write([]byte{0x10}); err != nil { *errorChan <- err return } *commandChan <- *command }