Initial signer rewrite in Go
This commit is contained in:
commit
a89275a8e4
9 changed files with 557 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
vendor/
|
||||||
|
.idea/
|
15
Gopkg.lock
generated
Normal file
15
Gopkg.lock
generated
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/goburrow/serial"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "5efbe925ecf714f8ba147bf2226f2e7afc7111bc"
|
||||||
|
version = "v0.1.0"
|
||||||
|
|
||||||
|
[solve-meta]
|
||||||
|
analyzer-name = "dep"
|
||||||
|
analyzer-version = 1
|
||||||
|
inputs-digest = "efa8bdd22b6fa2a5e6f86ba25724a05fca0ebabf4d436332aa90ded96fafa94d"
|
||||||
|
solver-name = "gps-cdcl"
|
||||||
|
solver-version = 1
|
34
Gopkg.toml
Normal file
34
Gopkg.toml
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# Gopkg.toml example
|
||||||
|
#
|
||||||
|
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||||
|
# for detailed Gopkg.toml documentation.
|
||||||
|
#
|
||||||
|
# required = ["github.com/user/thing/cmd/thing"]
|
||||||
|
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project"
|
||||||
|
# version = "1.0.0"
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project2"
|
||||||
|
# branch = "dev"
|
||||||
|
# source = "github.com/myfork/project2"
|
||||||
|
#
|
||||||
|
# [[override]]
|
||||||
|
# name = "github.com/x/y"
|
||||||
|
# version = "2.4.0"
|
||||||
|
#
|
||||||
|
# [prune]
|
||||||
|
# non-go = false
|
||||||
|
# go-tests = true
|
||||||
|
# unused-packages = true
|
||||||
|
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/goburrow/serial"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[prune]
|
||||||
|
go-tests = true
|
||||||
|
unused-packages = true
|
106
client/main.go
Normal file
106
client/main.go
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"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
|
||||||
|
message 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.StringVar(&message, "m", "serial", "message")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
config := serial.Config{
|
||||||
|
Address: address,
|
||||||
|
BaudRate: baudrate,
|
||||||
|
DataBits: databits,
|
||||||
|
StopBits: stopbits,
|
||||||
|
Parity: parity,
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
log.Printf("connecting %+v", config)
|
||||||
|
port, err := serial.Open(&config)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Println("connected")
|
||||||
|
defer func() {
|
||||||
|
err := port.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Println("closed")
|
||||||
|
}()
|
||||||
|
|
||||||
|
if _, err = port.Write([]byte{0x02}); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := make([]byte, 1)
|
||||||
|
if _, err = port.Read(resp); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
request := datastructures.NewNulRequest()
|
||||||
|
timeout := time.After(2700 * time.Millisecond)
|
||||||
|
errorChannel := make(chan error, 1)
|
||||||
|
responseChannel := make(chan *datastructures.SignerResponse, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if response, err := SendRequest(&port, request); err != nil {
|
||||||
|
errorChannel <- err
|
||||||
|
} else {
|
||||||
|
responseChannel <- response
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-timeout:
|
||||||
|
log.Fatal("timeout")
|
||||||
|
case err := <-errorChannel:
|
||||||
|
log.Fatal(err)
|
||||||
|
case response := <- responseChannel:
|
||||||
|
if err := Process(response); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requestBytes := request.Serialize()
|
||||||
|
|
||||||
|
if _, err = port.Write(requestBytes); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = port.Write([]byte{datastructures.CalculateXorCheckSum([][]byte{requestBytes})}); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = port.Write([]byte(shared.MagicTrailer)); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Process(response *datastructures.SignerResponse) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendRequest(port *serial.Port, request *datastructures.SignerRequest) (*datastructures.SignerResponse, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
28
datastructures/common.go
Normal file
28
datastructures/common.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package datastructures
|
||||||
|
|
||||||
|
import "encoding/binary"
|
||||||
|
|
||||||
|
type Action uint8
|
||||||
|
|
||||||
|
const ActionNul = Action(0)
|
||||||
|
|
||||||
|
func encode24BitLength(data []byte) []byte {
|
||||||
|
lengthBytes := make([]byte, 4)
|
||||||
|
binary.BigEndian.PutUint32(lengthBytes, uint32(len(data)))
|
||||||
|
return lengthBytes[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate length from 24 bits of data in network byte order
|
||||||
|
func decode24BitLength(bytes []byte) int {
|
||||||
|
return int(binary.BigEndian.Uint32([]byte{0x0, bytes[0], bytes[1], bytes[2]}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func CalculateXorCheckSum(byteBlocks [][]byte) byte {
|
||||||
|
var result byte = 0x0
|
||||||
|
for _, byteBlock := range byteBlocks {
|
||||||
|
for _, b := range byteBlock {
|
||||||
|
result ^= b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
80
datastructures/signerrequest.go
Normal file
80
datastructures/signerrequest.go
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
package datastructures
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SignerRequest struct {
|
||||||
|
Version uint8
|
||||||
|
Action Action
|
||||||
|
System uint8
|
||||||
|
Root uint8
|
||||||
|
Configuration uint8
|
||||||
|
Parameter1 uint8
|
||||||
|
Parameter2 uint16
|
||||||
|
Parameter3 uint8
|
||||||
|
Content1 string
|
||||||
|
Content2 string
|
||||||
|
Content3 string
|
||||||
|
}
|
||||||
|
|
||||||
|
func SignerRequestFromData(lengthBytes []byte, blockData []byte, checkSum byte) (*SignerRequest, error) {
|
||||||
|
headerLength := decode24BitLength(blockData[0:3])
|
||||||
|
headerBytes := blockData[3 : 3+headerLength]
|
||||||
|
|
||||||
|
contentBytes := blockData[3+headerLength:]
|
||||||
|
content1Length := decode24BitLength(contentBytes[0:3])
|
||||||
|
content1 := string(contentBytes[3 : 3+content1Length])
|
||||||
|
|
||||||
|
content2Offset := 3 + content1Length
|
||||||
|
content2Length := decode24BitLength(contentBytes[content2Offset : content2Offset+3])
|
||||||
|
content2 := string(contentBytes[3+content2Offset : 3+content2Offset+content2Length])
|
||||||
|
|
||||||
|
content3Offset := 3 + content2Offset + content2Length
|
||||||
|
content3Length := decode24BitLength(contentBytes[content3Offset : content3Offset+3])
|
||||||
|
content3 := string(contentBytes[3+content3Offset : 3+content3Offset+content3Length])
|
||||||
|
|
||||||
|
calculated := CalculateXorCheckSum([][]byte{lengthBytes, blockData})
|
||||||
|
if checkSum != calculated {
|
||||||
|
return nil, errors.New(fmt.Sprintf("invalid checksum expected 0x%x got 0x%x", calculated, checkSum))
|
||||||
|
}
|
||||||
|
return &SignerRequest{
|
||||||
|
Version: headerBytes[0],
|
||||||
|
Action: Action(headerBytes[1]),
|
||||||
|
System: headerBytes[2],
|
||||||
|
Root: headerBytes[3],
|
||||||
|
Configuration: headerBytes[4],
|
||||||
|
Parameter1: headerBytes[5],
|
||||||
|
Parameter2: binary.BigEndian.Uint16([]byte{headerBytes[6], headerBytes[7]}),
|
||||||
|
Parameter3: headerBytes[8],
|
||||||
|
Content1: content1,
|
||||||
|
Content2: content2,
|
||||||
|
Content3: content3,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r SignerRequest) Serialize() []byte {
|
||||||
|
parameter2Bytes := make([]byte, 2)
|
||||||
|
binary.BigEndian.PutUint16(parameter2Bytes, r.Parameter2)
|
||||||
|
headerBytes := bytes.Join([][]byte{
|
||||||
|
{r.Version, byte(r.Action), r.System, r.Root, r.Configuration, r.Parameter1},
|
||||||
|
parameter2Bytes, {r.Parameter3}}, []byte{})
|
||||||
|
content1Bytes := []byte(r.Content1)
|
||||||
|
content2Bytes := []byte(r.Content2)
|
||||||
|
content3Bytes := []byte(r.Content3)
|
||||||
|
blockBytes := bytes.Join([][]byte{
|
||||||
|
encode24BitLength(headerBytes), headerBytes,
|
||||||
|
encode24BitLength(content1Bytes), content1Bytes,
|
||||||
|
encode24BitLength(content2Bytes), content2Bytes,
|
||||||
|
encode24BitLength(content3Bytes), content3Bytes,
|
||||||
|
}, []byte{})
|
||||||
|
return bytes.Join([][]byte{encode24BitLength(blockBytes), blockBytes}, []byte{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNulRequest() *SignerRequest {
|
||||||
|
return &SignerRequest{Version: 1, Action: ActionNul, Content1: time.Now().UTC().Format("010203042006.05")}
|
||||||
|
}
|
63
datastructures/signerresponse.go
Normal file
63
datastructures/signerresponse.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package datastructures
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SignerResponse struct {
|
||||||
|
Version uint8
|
||||||
|
Action Action
|
||||||
|
Reserved1 uint8
|
||||||
|
Reserved2 uint8
|
||||||
|
Content1 string
|
||||||
|
Content2 string
|
||||||
|
Content3 string
|
||||||
|
}
|
||||||
|
|
||||||
|
func SignerResponseFromData(lengthBytes []byte, blockData []byte, checkSum byte) (*SignerResponse, error) {
|
||||||
|
headerLength := decode24BitLength(lengthBytes)
|
||||||
|
headerBytes := blockData[3 : 3+headerLength]
|
||||||
|
|
||||||
|
contentBytes := blockData[3+headerLength:]
|
||||||
|
content1Length := decode24BitLength(contentBytes[0:3])
|
||||||
|
content1 := string(contentBytes[3 : 3+content1Length])
|
||||||
|
|
||||||
|
content2Offset := 3 + content1Length
|
||||||
|
content2Length := decode24BitLength(contentBytes[content2Offset : content2Offset+3])
|
||||||
|
content2 := string(contentBytes[3+content2Offset : 3+content2Offset+content2Length])
|
||||||
|
|
||||||
|
content3Offset := 3 + content2Offset + content2Length
|
||||||
|
content3Length := decode24BitLength(contentBytes[content3Offset : content3Offset+3])
|
||||||
|
content3 := string(contentBytes[3+content3Offset : 3+content3Offset+content3Length])
|
||||||
|
|
||||||
|
calculated := CalculateXorCheckSum([][]byte{lengthBytes, blockData})
|
||||||
|
if checkSum != calculated {
|
||||||
|
return nil, errors.New(fmt.Sprintf("invalid checksum expected 0x%x got 0x%x", calculated, checkSum))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &SignerResponse{
|
||||||
|
Version: headerBytes[0],
|
||||||
|
Action: Action(headerBytes[1]),
|
||||||
|
Reserved1: headerBytes[2],
|
||||||
|
Reserved2: headerBytes[3],
|
||||||
|
Content1: content1,
|
||||||
|
Content2: content2,
|
||||||
|
Content3: content3,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r SignerResponse) Serialize() []byte {
|
||||||
|
headerBytes := []byte{r.Version, byte(r.Action), r.Reserved1, r.Reserved2}
|
||||||
|
content1Bytes := []byte(r.Content1)
|
||||||
|
content2Bytes := []byte(r.Content2)
|
||||||
|
content3Bytes := []byte(r.Content3)
|
||||||
|
blockBytes := bytes.Join([][]byte{
|
||||||
|
encode24BitLength(headerBytes), headerBytes,
|
||||||
|
encode24BitLength(content1Bytes), content1Bytes,
|
||||||
|
encode24BitLength(content2Bytes), content2Bytes,
|
||||||
|
encode24BitLength(content3Bytes), content3Bytes,
|
||||||
|
}, []byte{})
|
||||||
|
return bytes.Join([][]byte{encode24BitLength(blockBytes), blockBytes}, []byte{})
|
||||||
|
}
|
4
shared/shared.go
Normal file
4
shared/shared.go
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
package shared
|
||||||
|
|
||||||
|
const MagicTrailer = "rie4Ech7"
|
||||||
|
|
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…
Reference in a new issue