Refactor signer code into command and package
This commit is contained in:
parent
3a2578ae55
commit
38566f35ef
13 changed files with 476 additions and 276 deletions
13
.editorconfig
Normal file
13
.editorconfig
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = false
|
||||||
|
max_line_length = 120
|
||||||
|
tab_width = 4
|
||||||
|
|
||||||
|
[{*.go,*.go2}]
|
||||||
|
indent_style = tab
|
|
@ -2,7 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -45,13 +44,12 @@ func main() {
|
||||||
requestChannel := protocol.NewSignerProtocolRequestChannel()
|
requestChannel := protocol.NewSignerProtocolRequestChannel()
|
||||||
responseChannel := make(chan *datastructures.SignerResponse, 1)
|
responseChannel := make(chan *datastructures.SignerResponse, 1)
|
||||||
|
|
||||||
readWriteCloser := (io.ReadWriteCloser)(port)
|
|
||||||
clientProtocolConfig := protocol.NewSignerProtocolConfig()
|
clientProtocolConfig := protocol.NewSignerProtocolConfig()
|
||||||
if clientConfig.BufferSize != 0 {
|
if clientConfig.BufferSize != 0 {
|
||||||
clientProtocolConfig.BufferSize = int(clientConfig.BufferSize)
|
clientProtocolConfig.BufferSize = int(clientConfig.BufferSize)
|
||||||
}
|
}
|
||||||
protocolHandler := protocol.NewProtocolHandler(
|
protocolHandler := protocol.NewProtocolHandler(
|
||||||
requestChannel, &responseChannel, &readWriteCloser, clientProtocolConfig,
|
requestChannel, &responseChannel, port, clientProtocolConfig,
|
||||||
)
|
)
|
||||||
|
|
||||||
cancelChannel := make(chan os.Signal, 1)
|
cancelChannel := make(chan os.Signal, 1)
|
||||||
|
|
|
@ -2,7 +2,10 @@ package processing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.cacert.org/cacert-gosigner/datastructures"
|
"git.cacert.org/cacert-gosigner/datastructures"
|
||||||
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,7 +14,7 @@ func Process(response *datastructures.SignerResponse) (err error) {
|
||||||
logrus.Tracef("process response %+v", response)
|
logrus.Tracef("process response %+v", response)
|
||||||
|
|
||||||
switch response.Action {
|
switch response.Action {
|
||||||
case datastructures.ActionNul:
|
case shared.ActionNul:
|
||||||
logrus.Trace("received response for NUL request")
|
logrus.Trace("received response for NUL request")
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -3,12 +3,14 @@ package protocol
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.cacert.org/cacert-gosigner/datastructures"
|
|
||||||
"git.cacert.org/cacert-gosigner/shared"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"git.cacert.org/cacert-gosigner/datastructures"
|
||||||
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SignerProtocolHandler interface {
|
type SignerProtocolHandler interface {
|
||||||
|
@ -52,7 +54,7 @@ func (rc *SignerProtocolRequestChannel) IsClosed() bool {
|
||||||
type protocolHandler struct {
|
type protocolHandler struct {
|
||||||
requestChannel *SignerProtocolRequestChannel
|
requestChannel *SignerProtocolRequestChannel
|
||||||
responseChannel *chan *datastructures.SignerResponse
|
responseChannel *chan *datastructures.SignerResponse
|
||||||
serialConnection *io.ReadWriteCloser
|
serialConnection io.ReadWriteCloser
|
||||||
config *signerProtocolConfig
|
config *signerProtocolConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +78,12 @@ func (ph *protocolHandler) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProtocolHandler(requests *SignerProtocolRequestChannel, response *chan *datastructures.SignerResponse, serialConnection *io.ReadWriteCloser, config *signerProtocolConfig) SignerProtocolHandler {
|
func NewProtocolHandler(
|
||||||
|
requests *SignerProtocolRequestChannel,
|
||||||
|
response *chan *datastructures.SignerResponse,
|
||||||
|
serialConnection io.ReadWriteCloser,
|
||||||
|
config *signerProtocolConfig,
|
||||||
|
) SignerProtocolHandler {
|
||||||
return &protocolHandler{
|
return &protocolHandler{
|
||||||
requestChannel: requests,
|
requestChannel: requests,
|
||||||
responseChannel: response,
|
responseChannel: response,
|
||||||
|
@ -123,13 +130,13 @@ func (ph *protocolHandler) HandleSignerProtocol() error {
|
||||||
|
|
||||||
func (ph *protocolHandler) sendHandshake() (err error) {
|
func (ph *protocolHandler) sendHandshake() (err error) {
|
||||||
var bytesWritten, bytesRead int
|
var bytesWritten, bytesRead int
|
||||||
if bytesWritten, err = (*ph.serialConnection).Write([]byte{shared.HandshakeByte}); err != nil {
|
if bytesWritten, err = ph.serialConnection.Write([]byte{shared.HandshakeByte}); err != nil {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
log.Tracef("wrote %d bytes of handshake info", bytesWritten)
|
log.Tracef("wrote %d bytes of handshake info", bytesWritten)
|
||||||
}
|
}
|
||||||
data := make([]byte, 1)
|
data := make([]byte, 1)
|
||||||
if bytesRead, err = (*ph.serialConnection).Read(data); err != nil {
|
if bytesRead, err = ph.serialConnection.Read(data); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Tracef("%d bytes read", bytesRead)
|
log.Tracef("%d bytes read", bytesRead)
|
||||||
|
@ -142,13 +149,13 @@ func (ph *protocolHandler) sendHandshake() (err error) {
|
||||||
|
|
||||||
func (ph *protocolHandler) sendRequest(requestBytes *[]byte) error {
|
func (ph *protocolHandler) sendRequest(requestBytes *[]byte) error {
|
||||||
for {
|
for {
|
||||||
if length, err := (*ph.serialConnection).Write(*requestBytes); err != nil {
|
if length, err := ph.serialConnection.Write(*requestBytes); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
log.Tracef("wrote %d request bytes", length)
|
log.Tracef("wrote %d request bytes", length)
|
||||||
}
|
}
|
||||||
|
|
||||||
if length, err := (*ph.serialConnection).Write([]byte{
|
if length, err := ph.serialConnection.Write([]byte{
|
||||||
datastructures.CalculateXorCheckSum([][]byte{*requestBytes}),
|
datastructures.CalculateXorCheckSum([][]byte{*requestBytes}),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -156,7 +163,7 @@ func (ph *protocolHandler) sendRequest(requestBytes *[]byte) error {
|
||||||
log.Tracef("wrote %d checksum bytes", length)
|
log.Tracef("wrote %d checksum bytes", length)
|
||||||
}
|
}
|
||||||
|
|
||||||
if length, err := (*ph.serialConnection).Write([]byte(shared.MagicTrailer)); err != nil {
|
if length, err := ph.serialConnection.Write([]byte(shared.MagicTrailer)); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
log.Tracef("wrote %d trailer bytes", length)
|
log.Tracef("wrote %d trailer bytes", length)
|
||||||
|
@ -184,7 +191,7 @@ func (ph *protocolHandler) waitForResponseHandshake() (err error) {
|
||||||
log.Warnf("received invalid handshake byte 0x%x", data[0])
|
log.Warnf("received invalid handshake byte 0x%x", data[0])
|
||||||
return UnExpectedHandshakeByte{data[0]}
|
return UnExpectedHandshakeByte{data[0]}
|
||||||
}
|
}
|
||||||
if err = shared.SendByte(ph.serialConnection, shared.AckByte); err != nil {
|
if err = shared.SendBytes(ph.serialConnection, []byte{shared.AckByte}); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,7 +236,7 @@ func (ph *protocolHandler) readResponse() (*[]byte, *[]byte, byte, error) {
|
||||||
if calculatedChecksum != checkSum {
|
if calculatedChecksum != checkSum {
|
||||||
return nil, nil, 0, fmt.Errorf("calculated checksum mismatch 0x%x vs 0x%x", calculatedChecksum, checkSum)
|
return nil, nil, 0, fmt.Errorf("calculated checksum mismatch 0x%x vs 0x%x", calculatedChecksum, checkSum)
|
||||||
}
|
}
|
||||||
if err := shared.SendByte(ph.serialConnection, shared.AckByte); err != nil {
|
if err := shared.SendBytes(ph.serialConnection, []byte{shared.AckByte}); err != nil {
|
||||||
return nil, nil, 0, err
|
return nil, nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
71
cmd/signer/main.go
Normal file
71
cmd/signer/main.go
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"go.bug.st/serial"
|
||||||
|
|
||||||
|
"git.cacert.org/cacert-gosigner/signer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
address string
|
||||||
|
baudRate int
|
||||||
|
)
|
||||||
|
|
||||||
|
flag.StringVar(&address, "a", "/dev/ttyUSB0", "address")
|
||||||
|
flag.IntVar(&baudRate, "b", 115200, "baud rate")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
log.SetFormatter(&log.TextFormatter{
|
||||||
|
DisableColors: true,
|
||||||
|
FullTimestamp: true,
|
||||||
|
})
|
||||||
|
log.SetLevel(log.TraceLevel)
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
err := port.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("could not close port: %v", err)
|
||||||
|
}
|
||||||
|
log.Info("serial port closed")
|
||||||
|
}()
|
||||||
|
|
||||||
|
done := make(chan bool)
|
||||||
|
quit := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(quit, os.Interrupt)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-quit
|
||||||
|
log.Info("server is shutting down...")
|
||||||
|
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
signerProcess := signer.NewSignerProcess(port)
|
||||||
|
signerProcess.MainLoop()
|
||||||
|
|
||||||
|
close(quit)
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-done
|
||||||
|
log.Infoln("server stopped")
|
||||||
|
}
|
|
@ -2,27 +2,6 @@ package datastructures
|
||||||
|
|
||||||
import "encoding/binary"
|
import "encoding/binary"
|
||||||
|
|
||||||
type Action uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
ActionNul = Action(0)
|
|
||||||
ActionSign = Action(1)
|
|
||||||
ActionRevoke = Action(2)
|
|
||||||
)
|
|
||||||
|
|
||||||
func (a Action) String() string {
|
|
||||||
switch a {
|
|
||||||
case ActionNul:
|
|
||||||
return "NUL"
|
|
||||||
case ActionSign:
|
|
||||||
return "SIGN"
|
|
||||||
case ActionRevoke:
|
|
||||||
return "REVOKE"
|
|
||||||
default:
|
|
||||||
return "unknown"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func encode24BitLength(data []byte) []byte {
|
func encode24BitLength(data []byte) []byte {
|
||||||
lengthBytes := make([]byte, 4)
|
lengthBytes := make([]byte, 4)
|
||||||
binary.BigEndian.PutUint32(lengthBytes, uint32(len(data)))
|
binary.BigEndian.PutUint32(lengthBytes, uint32(len(data)))
|
||||||
|
|
|
@ -3,14 +3,14 @@ package datastructures
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SignerRequest struct {
|
type SignerRequest struct {
|
||||||
Version uint8
|
Version uint8
|
||||||
Action Action
|
Action shared.Action
|
||||||
System uint8
|
System uint8
|
||||||
Root uint8
|
Root uint8
|
||||||
Configuration uint8
|
Configuration uint8
|
||||||
|
@ -22,9 +22,7 @@ type SignerRequest struct {
|
||||||
Content3 string
|
Content3 string
|
||||||
}
|
}
|
||||||
|
|
||||||
const protocolVersion = 1
|
func SignerRequestFromData(blockData []byte) (*SignerRequest, error) {
|
||||||
|
|
||||||
func SignerRequestFromData(lengthBytes []byte, blockData []byte, checkSum byte) (*SignerRequest, error) {
|
|
||||||
headerLength := Decode24BitLength(blockData[0:3])
|
headerLength := Decode24BitLength(blockData[0:3])
|
||||||
headerBytes := blockData[3 : 3+headerLength]
|
headerBytes := blockData[3 : 3+headerLength]
|
||||||
|
|
||||||
|
@ -40,13 +38,9 @@ func SignerRequestFromData(lengthBytes []byte, blockData []byte, checkSum byte)
|
||||||
content3Length := Decode24BitLength(contentBytes[content3Offset : content3Offset+3])
|
content3Length := Decode24BitLength(contentBytes[content3Offset : content3Offset+3])
|
||||||
content3 := string(contentBytes[3+content3Offset : 3+content3Offset+content3Length])
|
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{
|
return &SignerRequest{
|
||||||
Version: headerBytes[0],
|
Version: headerBytes[0],
|
||||||
Action: Action(headerBytes[1]),
|
Action: shared.Action(headerBytes[1]),
|
||||||
System: headerBytes[2],
|
System: headerBytes[2],
|
||||||
Root: headerBytes[3],
|
Root: headerBytes[3],
|
||||||
Configuration: headerBytes[4],
|
Configuration: headerBytes[4],
|
||||||
|
@ -79,8 +73,8 @@ func (r SignerRequest) Serialize() []byte {
|
||||||
|
|
||||||
func NewNulRequest() *SignerRequest {
|
func NewNulRequest() *SignerRequest {
|
||||||
return &SignerRequest{
|
return &SignerRequest{
|
||||||
Version: protocolVersion,
|
Version: shared.ProtocolVersion,
|
||||||
Action: ActionNul,
|
Action: shared.ActionNul,
|
||||||
Content1: time.Now().UTC().Format("010203042006.05"),
|
Content1: time.Now().UTC().Format("010203042006.05"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,13 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SignerResponse struct {
|
type SignerResponse struct {
|
||||||
Version uint8
|
Version uint8
|
||||||
Action Action
|
Action shared.Action
|
||||||
Reserved1 uint8
|
Reserved1 uint8
|
||||||
Reserved2 uint8
|
Reserved2 uint8
|
||||||
Content1 string
|
Content1 string
|
||||||
|
@ -45,7 +47,7 @@ func SignerResponseFromData(lengthBytes []byte, blockData []byte, checkSum byte)
|
||||||
|
|
||||||
return &SignerResponse{
|
return &SignerResponse{
|
||||||
Version: headerBytes[0],
|
Version: headerBytes[0],
|
||||||
Action: Action(headerBytes[1]),
|
Action: shared.Action(headerBytes[1]),
|
||||||
Reserved1: headerBytes[2],
|
Reserved1: headerBytes[2],
|
||||||
Reserved2: headerBytes[3],
|
Reserved2: headerBytes[3],
|
||||||
Content1: content[0],
|
Content1: content[0],
|
||||||
|
|
37
shared/io.go
37
shared/io.go
|
@ -2,30 +2,37 @@ package shared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// receive at maximum the requested number of bytes from serial port and stop after the given timeout
|
// receive at maximum the requested number of bytes from serial port and stop after the given timeout
|
||||||
func ReceiveBytes(port *io.ReadWriteCloser, count int, timeout time.Duration) ([]byte, error) {
|
func ReceiveBytes(port io.Reader, count int, timeout time.Duration) ([]byte, error) {
|
||||||
readCh := make(chan []byte, 1)
|
readCh := make(chan []byte, 1)
|
||||||
errCh := make(chan error, 1)
|
errCh := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
data := make([]byte, count)
|
sumRead := 0
|
||||||
if readBytes, err := (*port).Read(data); err != nil {
|
for {
|
||||||
errCh <- err
|
data := make([]byte, count)
|
||||||
} else if readBytes > 0 {
|
if readBytes, err := port.Read(data); err != nil {
|
||||||
log.Tracef("%d bytes read", readBytes)
|
errCh <- err
|
||||||
readCh <- data[0:readBytes]
|
} else if readBytes > 0 {
|
||||||
} else {
|
log.Tracef("%d bytes read", readBytes)
|
||||||
readCh <- make([]byte, 0)
|
sumRead += readBytes
|
||||||
|
readCh <- data[0:readBytes]
|
||||||
|
} else {
|
||||||
|
readCh <- make([]byte, 0)
|
||||||
|
}
|
||||||
|
if sumRead >= count {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}()
|
}()
|
||||||
select {
|
select {
|
||||||
case <-time.After(timeout):
|
case <-time.After(timeout):
|
||||||
return nil, fmt.Errorf("timeout passed %v: %v", timeout)
|
return nil, fmt.Errorf("timeout passed %v", timeout)
|
||||||
case err := <-errCh:
|
case err := <-errCh:
|
||||||
return nil, err
|
return nil, err
|
||||||
case data := <-readCh:
|
case data := <-readCh:
|
||||||
|
@ -33,11 +40,11 @@ func ReceiveBytes(port *io.ReadWriteCloser, count int, timeout time.Duration) ([
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendByte(port *io.ReadWriteCloser, data byte) error {
|
func SendBytes(port io.Writer, data []byte) error {
|
||||||
if bytesWritten, err := (*port).Write([]byte{data}); err != nil {
|
if bytesWritten, err := port.Write(data); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
log.Tracef("wrote %d bytes of handshake info", bytesWritten)
|
log.Tracef("wrote %d bytes", bytesWritten)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,36 @@
|
||||||
package shared
|
package shared
|
||||||
|
|
||||||
const MagicTrailer = "rie4Ech7"
|
const (
|
||||||
|
ProtocolVersion = 1
|
||||||
|
|
||||||
const HandshakeByte = 0x02
|
HandshakeByte = 0x02
|
||||||
const AckByte = 0x10
|
AckByte = 0x10
|
||||||
const ResendByte = 0x11
|
ResendByte = 0x11
|
||||||
|
|
||||||
|
MagicTrailer = "rie4Ech7"
|
||||||
|
|
||||||
|
LengthFieldSize = 3
|
||||||
|
CheckSumFieldSize = 1
|
||||||
|
TrailerFieldSize = len(MagicTrailer)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Action uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
ActionNul = Action(0)
|
||||||
|
ActionSign = Action(1)
|
||||||
|
ActionRevoke = Action(2)
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a Action) String() string {
|
||||||
|
switch a {
|
||||||
|
case ActionNul:
|
||||||
|
return "NUL"
|
||||||
|
case ActionSign:
|
||||||
|
return "SIGN"
|
||||||
|
case ActionRevoke:
|
||||||
|
return "REVOKE"
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
90
signer/command_processor.go
Normal file
90
signer/command_processor.go
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
package signer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"git.cacert.org/cacert-gosigner/datastructures"
|
||||||
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The CommandProcessor takes parsed protocol data and executes the actual
|
||||||
|
// functionality.
|
||||||
|
type CommandProcessor struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the signer request
|
||||||
|
func (p *CommandProcessor) Process(command datastructures.SignerRequest) (
|
||||||
|
response *datastructures.SignerResponse,
|
||||||
|
err error,
|
||||||
|
) {
|
||||||
|
log.Infof("analyze %+v", command)
|
||||||
|
|
||||||
|
switch command.Action {
|
||||||
|
case shared.ActionNul:
|
||||||
|
response, err = p.handleNulAction(command)
|
||||||
|
return
|
||||||
|
case shared.ActionSign:
|
||||||
|
response, err = p.handleSignAction(command)
|
||||||
|
return
|
||||||
|
case shared.ActionRevoke:
|
||||||
|
response, err = p.handleRevokeAction(command)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
return nil, errors.New(fmt.Sprintf(
|
||||||
|
"unsupported Action 0x%02x %s",
|
||||||
|
int(command.Action),
|
||||||
|
command.Action,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CommandProcessor) handleNulAction(command datastructures.SignerRequest) (
|
||||||
|
*datastructures.SignerResponse,
|
||||||
|
error,
|
||||||
|
) {
|
||||||
|
var timeSpec unix.Timespec
|
||||||
|
err := unix.ClockGettime(unix.CLOCK_REALTIME, &timeSpec)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("could not get system time: %v", err)
|
||||||
|
}
|
||||||
|
log.Debugf("current system time is %v", timeSpec)
|
||||||
|
// TODO: calculate the actual system time from the payload
|
||||||
|
_, _, e1 := unix.Syscall(
|
||||||
|
unix.SYS_CLOCK_SETTIME,
|
||||||
|
uintptr(unix.CLOCK_REALTIME),
|
||||||
|
uintptr(unsafe.Pointer(&timeSpec)),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if e1 != 0 {
|
||||||
|
log.Errorf("could not set system time: %v", e1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &datastructures.SignerResponse{
|
||||||
|
Version: command.Version, Action: command.Action,
|
||||||
|
Content1: "", Content2: "", Content3: ""}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *CommandProcessor) handleSignAction(
|
||||||
|
command datastructures.SignerRequest,
|
||||||
|
) (
|
||||||
|
*datastructures.SignerResponse,
|
||||||
|
error,
|
||||||
|
) {
|
||||||
|
log.Debugf("handle sign call: %v", command)
|
||||||
|
return nil, errors.New("not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *CommandProcessor) handleRevokeAction(
|
||||||
|
command datastructures.SignerRequest,
|
||||||
|
) (
|
||||||
|
*datastructures.SignerResponse,
|
||||||
|
error,
|
||||||
|
) {
|
||||||
|
log.Debugf("handle revoke call: %v", command)
|
||||||
|
return nil, errors.New("not implemented yet")
|
||||||
|
}
|
205
signer/main.go
205
signer/main.go
|
@ -1,205 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
212
signer/port_handler.go
Normal file
212
signer/port_handler.go
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
package signer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"git.cacert.org/cacert-gosigner/datastructures"
|
||||||
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
waitForHandShake = time.Second * 5
|
||||||
|
waitForInitialByte = time.Second * 20
|
||||||
|
waitForLengthBytes = time.Second * 2
|
||||||
|
waitForChecksumByte = time.Second * 2
|
||||||
|
waitForTrailerBytes = time.Second * 2
|
||||||
|
waitForBlock = time.Minute * 1
|
||||||
|
|
||||||
|
maxTriesPerBlock = 10_000
|
||||||
|
)
|
||||||
|
|
||||||
|
// The PortHandler takes care of reading and writing of raw bytes from/to a serial port
|
||||||
|
type PortHandler struct {
|
||||||
|
port io.ReadWriteCloser
|
||||||
|
processor *CommandProcessor
|
||||||
|
errors chan error
|
||||||
|
commandChan chan *datastructures.SignerRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PortHandler) MainLoop() {
|
||||||
|
count := 0
|
||||||
|
for {
|
||||||
|
go p.Receive()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case command := <-p.commandChan:
|
||||||
|
if command == nil {
|
||||||
|
log.Infof("command channel has been closed. stopping execution.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debugf("received command %v", command)
|
||||||
|
response, err := p.processor.Process(*command)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error processing command: %v", err)
|
||||||
|
close(p.commandChan)
|
||||||
|
} else {
|
||||||
|
_ = p.SendResponse(response)
|
||||||
|
}
|
||||||
|
case err := <-p.errors:
|
||||||
|
log.Errorf("error from io goroutine %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
count++
|
||||||
|
log.Infof("%d requests processed. Waiting for next request ...", count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive a request and generate a request data structure
|
||||||
|
func (p *PortHandler) Receive() {
|
||||||
|
header, err := shared.ReceiveBytes(p.port, 1, waitForInitialByte)
|
||||||
|
if err != nil {
|
||||||
|
p.errors <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if header[0] != shared.HandshakeByte {
|
||||||
|
p.errors <- fmt.Errorf(
|
||||||
|
"unexpected byte 0x%x expected 0x%x",
|
||||||
|
header[0],
|
||||||
|
shared.HandshakeByte,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tries := maxTriesPerBlock
|
||||||
|
|
||||||
|
var command *datastructures.SignerRequest
|
||||||
|
for {
|
||||||
|
if tries <= 0 {
|
||||||
|
p.errors <- errors.New("tried reading block too often")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
tries--
|
||||||
|
if err := shared.SendBytes(p.port, []byte{shared.AckByte}); err != nil {
|
||||||
|
p.errors <- fmt.Errorf("could not write ACK byte: %v", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
lengthBytes, err := shared.ReceiveBytes(p.port, shared.LengthFieldSize, waitForLengthBytes)
|
||||||
|
if err != nil {
|
||||||
|
p.errors <- fmt.Errorf("could not read lenght bytes: %v", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
blockLength := datastructures.Decode24BitLength(lengthBytes)
|
||||||
|
blockData, err := shared.ReceiveBytes(p.port, blockLength, waitForBlock)
|
||||||
|
if err != nil {
|
||||||
|
p.errors <- fmt.Errorf("could not read data block: %v", err)
|
||||||
|
if !p.requestResend() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
checkSum, err := shared.ReceiveBytes(p.port, shared.CheckSumFieldSize, waitForChecksumByte)
|
||||||
|
if err != nil {
|
||||||
|
p.errors <- fmt.Errorf("could not read checksum byte: %v", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
calculated := datastructures.CalculateXorCheckSum([][]byte{lengthBytes, blockData})
|
||||||
|
if checkSum[0] != calculated {
|
||||||
|
p.errors <- fmt.Errorf("CRC error. expected 0x%02x, got 0x%02x", calculated, checkSum[0])
|
||||||
|
if !p.requestResend() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
trailer, err := shared.ReceiveBytes(p.port, shared.TrailerFieldSize, waitForTrailerBytes)
|
||||||
|
if err != nil {
|
||||||
|
p.errors <- fmt.Errorf("could not read trailer bytes: %v", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(trailer) != shared.MagicTrailer {
|
||||||
|
p.errors <- fmt.Errorf(
|
||||||
|
"BROKEN block detected, expected trailer bytes '%s' not found, got '%s' instead",
|
||||||
|
shared.MagicTrailer,
|
||||||
|
trailer,
|
||||||
|
)
|
||||||
|
if !p.requestResend() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
command, err = datastructures.SignerRequestFromData(blockData)
|
||||||
|
if err != nil {
|
||||||
|
p.errors <- fmt.Errorf("failed to parse block as signer request: %v", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := shared.SendBytes(p.port, []byte{shared.AckByte}); err != nil {
|
||||||
|
p.errors <- fmt.Errorf("failed to send ACK byte: %v", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
p.commandChan <- command
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PortHandler) requestResend() bool {
|
||||||
|
if err := shared.SendBytes(p.port, []byte{shared.ResendByte}); err != nil {
|
||||||
|
p.errors <- err
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a response to the client
|
||||||
|
func (p *PortHandler) SendResponse(response *datastructures.SignerResponse) error {
|
||||||
|
if err := shared.SendBytes(p.port, []byte{shared.HandshakeByte}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ack, err := shared.ReceiveBytes(p.port, 1, waitForHandShake); err != nil {
|
||||||
|
return err
|
||||||
|
} else if ack[0] != shared.AckByte {
|
||||||
|
return errors.New(fmt.Sprintf("invalid ack byte 0x%02x", ack[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
tryAgain := true
|
||||||
|
for tryAgain {
|
||||||
|
data := response.Serialize()
|
||||||
|
if err := shared.SendBytes(p.port, data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum := datastructures.CalculateXorCheckSum([][]byte{data})
|
||||||
|
if err := shared.SendBytes(p.port, []byte{checksum}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := shared.SendBytes(p.port, []byte(shared.MagicTrailer)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ack, err := shared.ReceiveBytes(p.port, 1, waitForHandShake); err != nil {
|
||||||
|
return err
|
||||||
|
} else if ack[0] == shared.AckByte {
|
||||||
|
tryAgain = false
|
||||||
|
} else if ack[0] != 0x11 {
|
||||||
|
return errors.New(fmt.Sprintf("invalid ack byte 0x%02x", ack[0]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSignerProcess(port io.ReadWriteCloser) *PortHandler {
|
||||||
|
errorChan := make(chan error)
|
||||||
|
return &PortHandler{
|
||||||
|
port: port,
|
||||||
|
errors: errorChan,
|
||||||
|
commandChan: make(chan *datastructures.SignerRequest, 1),
|
||||||
|
processor: &CommandProcessor{},
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue