cacert-gosigner/signer/main.go
Jan Dittberner 42d1e6e991 Refactor client into separate files
Add a main loop, move I/O code into io.go, move configuration into config.go.
Use shared.Decode24BitLength instead of manually decoding block lengths.
Fix response block decoding and checksum validation.
Add constants for commonly used byte values and use these in the signer and
the client.
2020-04-17 19:39:01 +02:00

201 lines
4.7 KiB
Go

package main
import (
"errors"
"flag"
"fmt"
"log"
"time"
"git.cacert.org/cacert-gosigner/datastructures"
"git.cacert.org/cacert-gosigner/shared"
"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] != 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)
if err != nil {
*errorChan <- err
return
}
blockLength := datastructures.Decode24BitLength(lengthBytes)
blockData, err := shared.ReceiveBytes(port, 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
}