package main import ( "encoding/binary" "errors" "flag" "fmt" "git.cacert.org/cacert-gosigner/datastructures" "git.cacert.org/cacert-gosigner/shared" "io/ioutil" "time" "github.com/goburrow/serial" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" ) type SerialConfig struct { Address string `yaml:"address"` BaudRate int `yaml:"baudrate"` DataBits int `yaml:"databits"` StopBits int `yaml:"stopbits"` Parity string `yaml:"parity"` } type ClientConfig struct { Serial SerialConfig `yaml:"serial_config"` Paranoid bool `yaml:"paranoid"` Debug bool `yaml:"debug"` GNUPGBinary string `yaml:"gnupg_bin"` OpenSSLBinary string `yaml:"openssl_bin"` MySQLDSN string `yaml:"mysql_dsn"` } var defaultConfig = ClientConfig{ Serial: SerialConfig{ Address: "/dev/ttyUSB0", BaudRate: 115200, DataBits: 8, StopBits: 1, Parity: "N", }, Paranoid: false, Debug: false, OpenSSLBinary: "/usr/bin/openssl", GNUPGBinary: "/usr/bin/gpg", MySQLDSN: ":@/database?parseTime=true", } const HandshakeByte = 0x02 const AckByte = 0x10 const NackByte = 0x11 func readConfig(configFile string) (config *ClientConfig, err error) { source, err := ioutil.ReadFile(configFile) if err != nil { log.Errorf("opening configuration file failed: %v", err) if exampleConfig, err := generateExampleConfig(configFile); err != nil { return nil, err } else { log.Info("starting with default config") return exampleConfig, nil } } if err := yaml.Unmarshal(source, &config); err != nil { return nil, fmt.Errorf("loading configuration file failed: %v", err) } return config, nil } func generateExampleConfig(configFile string) (config *ClientConfig, err error) { config = &defaultConfig configBytes, err := yaml.Marshal(config) if err != nil { log.Errorf("could not generate configuration data") return } log.Infof("example data for %s:\n\n---\n%s\n", configFile, configBytes) return } func main() { var configFile string flag.StringVar(&configFile, "c", "client.yaml", "client configuration file in YAML format") flag.Parse() var clientConfig *ClientConfig var serialConfig *serial.Config var err error if clientConfig, err = readConfig(configFile); err != nil { log.Panic(err) } serialConfig = &serial.Config{ Address: clientConfig.Serial.Address, BaudRate: clientConfig.Serial.BaudRate, DataBits: clientConfig.Serial.DataBits, StopBits: clientConfig.Serial.StopBits, Parity: clientConfig.Serial.Parity, Timeout: 30 * time.Second, } if clientConfig.Debug { log.SetLevel(log.DebugLevel) } log.Debugf("connecting %+v", serialConfig) port, err := serial.Open(serialConfig) if err != nil { log.Fatal(err) } log.Debug("connected") defer func() { err := port.Close() if err != nil { log.Fatal(err) } log.Debug("closed") }() request := datastructures.NewNulRequest() timeout := time.After(2700 * time.Millisecond) errorChannel := make(chan error, 1) responseChannel := make(chan *datastructures.SignerResponse, 1) go func() { if response, err := SendRequest(port, request); err != nil { errorChannel <- err } else { responseChannel <- response } }() select { case <-timeout: log.Fatal("timeout") case err := <-errorChannel: log.Fatal(err) case response := <-responseChannel: if err := Process(response); err != nil { log.Fatal(err) } } } func Process(response *datastructures.SignerResponse) error { log.Debugf("process %v", response) return nil } func sendHandShake(port serial.Port) error { log.Debug("Shaking hands ...") if length, err := port.Write([]byte{HandshakeByte}); err != nil { return fmt.Errorf("could not write handshake byte: %v", err) } else { log.Debugf("wrote %d handshake bytes", length) } handShakeResponse := make([]byte, 1) if length, err := port.Read(handShakeResponse); err != nil { return fmt.Errorf("failed to read handshake response: %v", err) } else { log.Debugf("read %d bytes", length) } if handShakeResponse[0] != AckByte { return fmt.Errorf("invalid handshake response expected 0x10 received %x", handShakeResponse[0]) } log.Debug("Handshake successful") return nil } func receiveResponse(port *serial.Port, responseChan *chan []byte, errorChan *chan error) { header, err := shared.ReceiveBytes(port, 1, 20) if err != nil { *errorChan <- err return } if header[0] != HandshakeByte { *errorChan <- fmt.Errorf("unexpected byte 0x%x expected 0x%x", header, HandshakeByte) } if _, err := (*port).Write([]byte{AckByte}); err != nil { *errorChan <- errors.New("could not write ACK") return } lengthBytes, err := shared.ReceiveBytes(port, 3, 2) if err != nil { *errorChan <- err return } blockLength := binary.BigEndian.Uint32([]byte{0x0, lengthBytes[0], lengthBytes[1], lengthBytes[2]}) blockData, err := shared.ReceiveBytes(port, int(blockLength), 5) if err != nil { *errorChan <- err return } checkSum, err := shared.ReceiveBytes(port, 1, 2) if err != nil { *errorChan <- err return } log.Debugf("block checksum is %d", checkSum) trailer, err := shared.ReceiveBytes(port, len(shared.MagicTrailer), 2) 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{AckByte}); err != nil { *errorChan <- fmt.Errorf("could not write ACK byte: %v", err) } *responseChan <- blockData return } func SendRequest(port serial.Port, request *datastructures.SignerRequest) (response *datastructures.SignerResponse, err error) { log.Debugf("send request %v to serial port %v", request, port) if err = sendHandShake(port); err != nil { return nil, err } requestBytes := request.Serialize() if length, err := port.Write(requestBytes); err != nil { log.Fatal(err) } else { log.Debugf("wrote %d request bytes", length) } if length, err := port.Write([]byte{datastructures.CalculateXorCheckSum([][]byte{requestBytes})}); err != nil { log.Fatal(err) } else { log.Debugf("wrote %d checksum bytes", length) } if length, err := port.Write([]byte(shared.MagicTrailer)); err != nil { log.Fatal(err) } else { log.Debugf("wrote %d trailer bytes", length) } header, err := shared.ReceiveBytes(&port, 1, 20) if err != nil { return nil, err } if header[0] != AckByte { return nil, fmt.Errorf("unexpected byte 0x%x expected 0x%x", header, AckByte) } responseChan := make(chan []byte, 1) errChan := make(chan error, 1) go receiveResponse(&port, &responseChan, &errChan) select { case responseData := <-responseChan: log.Debugf("response data: %v", responseData) case err := <-errChan: log.Errorf("%v", err) } return nil, nil }