Initial signer rewrite in Go
This commit is contained in:
commit
a89275a8e4
9 changed files with 557 additions and 0 deletions
225
signer/main.go
Normal file
225
signer/main.go
Normal file
|
@ -0,0 +1,225 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"git.cacert.org/cacert-gosigner/datastructures"
|
||||
"git.cacert.org/cacert-gosigner/shared"
|
||||
"io"
|
||||
"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 := 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 := 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 := receiveBytes(port, 1, 20)
|
||||
if err != nil {
|
||||
*errorChan <- err
|
||||
return
|
||||
}
|
||||
if header[0] != 0x02 {
|
||||
*errorChan <- errors.New(fmt.Sprintf("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 := receiveBytes(port, 3, 2)
|
||||
if err != nil {
|
||||
*errorChan <- err
|
||||
return
|
||||
}
|
||||
blockLength := binary.BigEndian.Uint32([]byte{0x0, lengthBytes[0], lengthBytes[1], lengthBytes[2]})
|
||||
blockData, err := receiveBytes(port, int(blockLength), 5)
|
||||
if err != nil {
|
||||
*errorChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
checkSum, err := 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 := 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
|
||||
}
|
||||
|
||||
// receive the requested number of bytes from serial port and stop after the given timeout in seconds
|
||||
func receiveBytes(port *serial.Port, count int, timeout time.Duration) ([]byte, error) {
|
||||
timeoutCh := time.After(timeout * time.Second)
|
||||
readCh := make(chan []byte, 1)
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
data := make([]byte, count)
|
||||
if _, err := io.ReadAtLeast(*port, data, count); err != nil {
|
||||
errCh <- err
|
||||
} else {
|
||||
readCh <- data
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-timeoutCh:
|
||||
return nil, errors.New("timeout")
|
||||
case err := <-errCh:
|
||||
return nil, err
|
||||
case data := <-readCh:
|
||||
return data, nil
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue