cacert-gosigner/signer/main.go

203 lines
4.7 KiB
Go

package main
import (
"encoding/binary"
"errors"
"flag"
"fmt"
"git.cacert.org/cacert-gosigner/datastructures"
"git.cacert.org/cacert-gosigner/shared"
"log"
"time"
"github.com/goburrow/serial"
)
var (
address string
baudrate int
databits int
stopbits int
parity string
)
func main() {
flag.StringVar(&address, "a", "/dev/ttyUSB0", "address")
flag.IntVar(&baudrate, "b", 115200, "baud rate")
flag.IntVar(&databits, "d", 8, "data bits")
flag.IntVar(&stopbits, "s", 1, "stop bits")
flag.StringVar(&parity, "p", "N", "parity (N/E/O)")
flag.Parse()
config := serial.Config{
Address: address,
BaudRate: baudrate,
DataBits: databits,
StopBits: stopbits,
Parity: parity,
Timeout: 5 * time.Minute,
}
log.Printf("connecting %+v", config)
port, err := serial.Open(&config)
if err != nil {
log.Fatal(err)
}
log.Println("connected")
count := 0
defer func() {
err := port.Close()
if err != nil {
log.Fatal(err)
}
log.Println("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)
go Receive(&port, &commandChan, &errChan)
select {
case command := <-commandChan:
response, err := Process(command)
if err != nil {
log.Printf("ERROR %v\n", err)
} else {
_ = SendResponse(&port, response)
}
case <-timeout:
log.Println("timeout in main loop")
break readLoop
case err := <-errChan:
log.Printf("ERROR %v\n", err)
}
count++
log.Printf("INFO %d requests processed. Waiting for next request ...\n", count)
}
}
// Send a response to the client
func SendResponse(port *serial.Port, response *datastructures.SignerResponse) error {
if _, err := (*port).Write([]byte{0x02}); err != nil {
return err
}
if ack, err := shared.ReceiveBytes(port, 1, 5); 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); 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.Printf("INFO analyze %+v\n", command)
switch command.Action {
case datastructures.ActionNul:
response, err = handleNulAction(command)
return
default:
return nil, errors.New(fmt.Sprintf("unsupported Action 0x%02x", 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 *serial.Port, commandChan *chan datastructures.SignerRequest, errorChan *chan error) {
header, err := shared.ReceiveBytes(port, 1, 20)
if err != nil {
*errorChan <- err
return
}
if header[0] != 0x02 {
*errorChan <- fmt.Errorf("unexpected byte 0x%x expected 0x02", header)
}
if _, err := (*port).Write([]byte{0x10}); 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
}
command, err := datastructures.SignerRequestFromData(lengthBytes, blockData, checkSum[0])
if err != nil {
*errorChan <- err
}
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{0x10}); err != nil {
*errorChan <- err
return
}
*commandChan <- *command
}