205 lines
4.9 KiB
Go
205 lines
4.9 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"time"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
"go.bug.st/serial"
|
|
|
|
"git.cacert.org/cacert-gosigner/datastructures"
|
|
"git.cacert.org/cacert-gosigner/shared"
|
|
)
|
|
|
|
var (
|
|
address string
|
|
baudrate int
|
|
)
|
|
|
|
func main() {
|
|
flag.StringVar(&address, "a", "/dev/ttyUSB0", "address")
|
|
flag.IntVar(&baudrate, "b", 115200, "baud rate")
|
|
flag.Parse()
|
|
|
|
log.SetFormatter(&log.TextFormatter{
|
|
DisableColors: true,
|
|
FullTimestamp: true,
|
|
})
|
|
|
|
serialMode := &serial.Mode{
|
|
BaudRate: baudrate,
|
|
DataBits: 8,
|
|
StopBits: serial.OneStopBit,
|
|
Parity: serial.NoParity,
|
|
}
|
|
log.Infof("connecting to %s using %+v", address, serialMode)
|
|
port, err := serial.Open(address, serialMode)
|
|
if err != nil {
|
|
log.Fatalf("could not open serial port: %v", err)
|
|
}
|
|
log.Info("connected")
|
|
|
|
count := 0
|
|
|
|
defer func() {
|
|
err := port.Close()
|
|
if err != nil {
|
|
log.Fatalf("could not close port: %v", err)
|
|
}
|
|
log.Info("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)
|
|
readWriteCloser := (io.ReadWriteCloser)(port)
|
|
go Receive(&readWriteCloser, &commandChan, &errChan)
|
|
|
|
select {
|
|
case command := <-commandChan:
|
|
response, err := Process(command)
|
|
if err != nil {
|
|
log.Errorf("error processing command: %v", err)
|
|
break readLoop
|
|
}
|
|
_ = SendResponse(&readWriteCloser, response)
|
|
case <-timeout:
|
|
log.Error("timeout in main loop")
|
|
break readLoop
|
|
case err := <-errChan:
|
|
log.Errorf("error from io goroutine %v", err)
|
|
}
|
|
|
|
count++
|
|
log.Infof("%d requests processed. Waiting for next request ...", count)
|
|
}
|
|
}
|
|
|
|
// Send a response to the client
|
|
func SendResponse(port *io.ReadWriteCloser, response *datastructures.SignerResponse) error {
|
|
if _, err := (*port).Write([]byte{0x02}); err != nil {
|
|
return err
|
|
}
|
|
|
|
if ack, err := shared.ReceiveBytes(port, 1, 5*time.Second); 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*time.Second); 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.Infof("analyze %+v", command)
|
|
|
|
switch command.Action {
|
|
case datastructures.ActionNul:
|
|
response, err = handleNulAction(command)
|
|
return
|
|
default:
|
|
return nil, errors.New(fmt.Sprintf(
|
|
"unsupported Action 0x%02x %s",
|
|
int(command.Action),
|
|
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 *io.ReadWriteCloser, commandChan *chan datastructures.SignerRequest, errorChan *chan error) {
|
|
header, err := shared.ReceiveBytes(port, 1, 20*time.Second)
|
|
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*time.Second)
|
|
if err != nil {
|
|
*errorChan <- err
|
|
return
|
|
}
|
|
blockLength := datastructures.Decode24BitLength(lengthBytes)
|
|
blockData, err := shared.ReceiveBytes(port, blockLength, 5*time.Second)
|
|
if err != nil {
|
|
*errorChan <- err
|
|
return
|
|
}
|
|
|
|
checkSum, err := shared.ReceiveBytes(port, 1, 2*time.Second)
|
|
if err != nil {
|
|
*errorChan <- err
|
|
return
|
|
}
|
|
|
|
command, err := datastructures.SignerRequestFromData(lengthBytes, blockData, checkSum[0])
|
|
if err != nil {
|
|
*errorChan <- err
|
|
return
|
|
}
|
|
|
|
trailer, err := shared.ReceiveBytes(port, len(shared.MagicTrailer), 2*time.Second)
|
|
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
|
|
}
|