cacert-gosigner/client/main.go

271 lines
6.7 KiB
Go

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: "<username>:<password>@/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
}