42d1e6e991
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.
201 lines
4.7 KiB
Go
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
|
|
}
|