diff --git a/client/config.go b/client/config.go new file mode 100644 index 0000000..810d9a6 --- /dev/null +++ b/client/config.go @@ -0,0 +1,84 @@ +package main + +import ( + "fmt" + "io/ioutil" + "time" + + "github.com/goburrow/serial" + "github.com/sirupsen/logrus" + "gopkg.in/yaml.v2" +) + +type SerialConfig struct { + Address string `yaml:"address"` + BaudRate int `yaml:"baudrate"` + DataBits int `yaml:"databits"` + StopBits int `yaml:"stopbits"` + Parity string `yaml:"parity"` +} + +type ClientConfig struct { + Serial SerialConfig `yaml:"serial_config"` + Paranoid bool `yaml:"paranoid"` + Debug bool `yaml:"debug"` + GNUPGBinary string `yaml:"gnupg_bin"` + OpenSSLBinary string `yaml:"openssl_bin"` + MySQLDSN string `yaml:"mysql_dsn"` +} + +var defaultConfig = ClientConfig{ + Serial: SerialConfig{ + Address: "/dev/ttyUSB0", + BaudRate: 115200, + DataBits: 8, + StopBits: 1, + Parity: "N", + }, + Paranoid: false, + Debug: false, + OpenSSLBinary: "/usr/bin/openssl", + GNUPGBinary: "/usr/bin/gpg", + MySQLDSN: ":@/database?parseTime=true", +} + +func generateExampleConfig(configFile string) (config *ClientConfig, err error) { + config = &defaultConfig + configBytes, err := yaml.Marshal(config) + if err != nil { + logrus.Errorf("could not generate configuration data") + return + } + + logrus.Infof("example data for %s:\n\n---\n%s\n", configFile, configBytes) + return +} + +func readConfig(configFile string) (config *ClientConfig, err error) { + source, err := ioutil.ReadFile(configFile) + if err != nil { + logrus.Errorf("opening configuration file failed: %v", err) + if exampleConfig, err := generateExampleConfig(configFile); err != nil { + return nil, err + } else { + logrus.Info("starting with default config") + return exampleConfig, nil + } + } + if err := yaml.Unmarshal(source, &config); err != nil { + return nil, fmt.Errorf("loading configuration file failed: %v", err) + } + + return config, nil +} + +func fillSerialConfig(clientConfig *ClientConfig) *serial.Config { + return &serial.Config{ + Address: clientConfig.Serial.Address, + BaudRate: clientConfig.Serial.BaudRate, + DataBits: clientConfig.Serial.DataBits, + StopBits: clientConfig.Serial.StopBits, + Parity: clientConfig.Serial.Parity, + Timeout: 30 * time.Second, + } +} diff --git a/client/io.go b/client/io.go new file mode 100644 index 0000000..73588fe --- /dev/null +++ b/client/io.go @@ -0,0 +1,98 @@ +package main + +import ( + "errors" + "fmt" + + "github.com/goburrow/serial" + log "github.com/sirupsen/logrus" + + "git.cacert.org/cacert-gosigner/datastructures" + "git.cacert.org/cacert-gosigner/shared" +) + +func sendHandShake(port serial.Port) error { + log.Debug("Shaking hands ...") + if length, err := port.Write([]byte{shared.HandshakeByte}); err != nil { + return fmt.Errorf("could not write handshake byte: %v", err) + } else { + log.Tracef("wrote %d handshake bytes", length) + } + handShakeResponse := make([]byte, 1) + if length, err := port.Read(handShakeResponse); err != nil { + return fmt.Errorf("failed to read handshake response: %v", err) + } else { + log.Tracef("read %d bytes", length) + } + if handShakeResponse[0] != shared.AckByte { + return fmt.Errorf("invalid handshake response expected 0x10 received %x", handShakeResponse[0]) + } + log.Debug("Handshake successful") + return nil +} + +func receiveResponse(port *serial.Port, responseChan *chan datastructures.SignerResponse, 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, shared.HandshakeByte) + } + log.Tracef("received handshake byte") + + if _, err := (*port).Write([]byte{shared.AckByte}); err != nil { + *errorChan <- errors.New("could not write ACK") + return + } + log.Tracef("sent ACK byte") + + lengthBytes, err := shared.ReceiveBytes(port, 3, 2) + if err != nil { + *errorChan <- err + return + } + blockLength := datastructures.Decode24BitLength(lengthBytes) + log.Tracef("received block length %d", blockLength) + + blockData, err := shared.ReceiveBytes(port, blockLength, 5) + if err != nil { + *errorChan <- err + return + } + log.Tracef("received bytes %v", blockData) + + checkSum, err := shared.ReceiveBytes(port, 1, 2) + if err != nil { + *errorChan <- err + return + } + log.Tracef("received checksum 0x%x", checkSum[0]) + + 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 + } + log.Tracef("received valid trailer bytes") + + if _, err := (*port).Write([]byte{shared.AckByte}); err != nil { + *errorChan <- fmt.Errorf("could not write ACK byte: %v", err) + return + } + log.Tracef("sent ACK byte") + + signerResponse, err := datastructures.SignerResponseFromData(lengthBytes, blockData, checkSum[0]) + if err != nil { + *errorChan <- err + return + } + log.Infof("received response of type %s", signerResponse.Action) + + *responseChan <- *signerResponse +} diff --git a/client/main.go b/client/main.go index 67b4237..59a82c1 100644 --- a/client/main.go +++ b/client/main.go @@ -1,86 +1,17 @@ package main import ( - "encoding/binary" - "errors" "flag" "fmt" + "time" + "git.cacert.org/cacert-gosigner/datastructures" "git.cacert.org/cacert-gosigner/shared" - "io/ioutil" - "time" "github.com/goburrow/serial" log "github.com/sirupsen/logrus" - "gopkg.in/yaml.v2" ) -type SerialConfig struct { - Address string `yaml:"address"` - BaudRate int `yaml:"baudrate"` - DataBits int `yaml:"databits"` - StopBits int `yaml:"stopbits"` - Parity string `yaml:"parity"` -} - -type ClientConfig struct { - Serial SerialConfig `yaml:"serial_config"` - Paranoid bool `yaml:"paranoid"` - Debug bool `yaml:"debug"` - GNUPGBinary string `yaml:"gnupg_bin"` - OpenSSLBinary string `yaml:"openssl_bin"` - MySQLDSN string `yaml:"mysql_dsn"` -} - -var defaultConfig = ClientConfig{ - Serial: SerialConfig{ - Address: "/dev/ttyUSB0", - BaudRate: 115200, - DataBits: 8, - StopBits: 1, - Parity: "N", - }, - Paranoid: false, - Debug: false, - OpenSSLBinary: "/usr/bin/openssl", - GNUPGBinary: "/usr/bin/gpg", - MySQLDSN: ":@/database?parseTime=true", -} - -const HandshakeByte = 0x02 -const AckByte = 0x10 -const NackByte = 0x11 - -func readConfig(configFile string) (config *ClientConfig, err error) { - source, err := ioutil.ReadFile(configFile) - if err != nil { - log.Errorf("opening configuration file failed: %v", err) - if exampleConfig, err := generateExampleConfig(configFile); err != nil { - return nil, err - } else { - log.Info("starting with default config") - return exampleConfig, nil - } - } - if err := yaml.Unmarshal(source, &config); err != nil { - return nil, fmt.Errorf("loading configuration file failed: %v", err) - } - - return config, nil -} - -func generateExampleConfig(configFile string) (config *ClientConfig, err error) { - config = &defaultConfig - configBytes, err := yaml.Marshal(config) - if err != nil { - log.Errorf("could not generate configuration data") - return - } - - log.Infof("example data for %s:\n\n---\n%s\n", configFile, configBytes) - return -} - func main() { var configFile string @@ -94,177 +25,127 @@ func main() { if clientConfig, err = readConfig(configFile); err != nil { log.Panic(err) } - serialConfig = &serial.Config{ - Address: clientConfig.Serial.Address, - BaudRate: clientConfig.Serial.BaudRate, - DataBits: clientConfig.Serial.DataBits, - StopBits: clientConfig.Serial.StopBits, - Parity: clientConfig.Serial.Parity, - Timeout: 30 * time.Second, - } + serialConfig = fillSerialConfig(clientConfig) if clientConfig.Debug { log.SetLevel(log.DebugLevel) } - log.Debugf("connecting %+v", serialConfig) + log.Infof("connecting to %s", serialConfig.Address) + log.Tracef("serial parameters %v", serialConfig) port, err := serial.Open(serialConfig) if err != nil { log.Fatal(err) } - log.Debug("connected") + log.Debug("serial port connected") defer func() { err := port.Close() if err != nil { log.Fatal(err) } - log.Debug("closed") + log.Debug("serial port closed") }() - request := datastructures.NewNulRequest() - timeout := time.After(2700 * time.Millisecond) errorChannel := make(chan error, 1) - responseChannel := make(chan *datastructures.SignerResponse, 1) + responseChannel := make(chan datastructures.SignerResponse, 1) + crlCheck := 0 - go func() { - if response, err := SendRequest(port, request); err != nil { - errorChannel <- err - } else { - responseChannel <- response + log.Debug("starting main loop") + + for { + requestChannel := make(chan datastructures.SignerRequest, 1) + + go HandleRequests(&port, &responseChannel, &errorChannel, &requestChannel) + + log.Debug("handling GPG database ...") + // HandleGPG(&requestChannel) + log.Debug("issuing certificates ...") + // HandleCertificates(&requestChannel) + log.Debug("revoking certificates ...") + // RevokeCertificates(&requestChannel) + + crlCheck++ + if crlCheck%100 == 0 { + log.Debug("refresh CRLs ...") + // RefreshCRLs(&requestChannel) } - }() - select { - case <-timeout: - log.Fatal("timeout") - case err := <-errorChannel: - log.Fatal(err) - case response := <-responseChannel: - if err := Process(response); err != nil { - log.Fatal(err) + log.Debug("send NUL request to keep connection open") + requestChannel <- *datastructures.NewNulRequest() + + select { + case response := <-responseChannel: + if err := Process(response); err != nil { + log.Error(err) + } + case err := <-errorChannel: + log.Error(err) + } + + log.Debug("sleep for 2.7 seconds") + time.Sleep(2700 * time.Millisecond) + } +} + +func Process(response datastructures.SignerResponse) (err error) { + log.Infof("process response of type %s", response.Action) + log.Tracef("process response %v", response) + + switch response.Action { + case datastructures.ActionNul: + log.Trace("received response for NUL request") + return + default: + return fmt.Errorf("unsupported action in response 0x%x", response.Action) + } +} + +func HandleRequests(port *serial.Port, responseChan *chan datastructures.SignerResponse, errorChan *chan error, requestChan *chan datastructures.SignerRequest) { + for { + select { + case request := <-*requestChan: + SendRequest(port, responseChan, errorChan, &request) } } } -func Process(response *datastructures.SignerResponse) error { - log.Debugf("process %v", response) - return nil -} +func SendRequest(port *serial.Port, responseChan *chan datastructures.SignerResponse, errorChan *chan error, request *datastructures.SignerRequest) { + log.Tracef("send request %v to serial port %v", *request, *port) + if err := sendHandShake(*port); err != nil { + *errorChan <- err + return + } -func sendHandShake(port serial.Port) error { - log.Debug("Shaking hands ...") - if length, err := port.Write([]byte{HandshakeByte}); err != nil { - return fmt.Errorf("could not write handshake byte: %v", err) + requestBytes := request.Serialize() + if length, err := (*port).Write(requestBytes); err != nil { + *errorChan <- err + return } else { - log.Debugf("wrote %d handshake bytes", length) + log.Tracef("wrote %d request bytes", length) + } + + if length, err := (*port).Write([]byte{datastructures.CalculateXorCheckSum([][]byte{requestBytes})}); err != nil { + *errorChan <- err + return + } else { + log.Tracef("wrote %d checksum bytes", length) + } + + if length, err := (*port).Write([]byte(shared.MagicTrailer)); err != nil { + *errorChan <- err + return + } else { + log.Tracef("wrote %d trailer bytes", length) } - handShakeResponse := make([]byte, 1) - if length, err := port.Read(handShakeResponse); err != nil { - return fmt.Errorf("failed to read handshake response: %v", err) - } else { - log.Debugf("read %d bytes", length) - } - if handShakeResponse[0] != AckByte { - return fmt.Errorf("invalid handshake response expected 0x10 received %x", handShakeResponse[0]) - } - log.Debug("Handshake successful") - return nil -} -func receiveResponse(port *serial.Port, responseChan *chan []byte, errorChan *chan error) { header, err := shared.ReceiveBytes(port, 1, 20) if err != nil { *errorChan <- err return } - if header[0] != HandshakeByte { - *errorChan <- fmt.Errorf("unexpected byte 0x%x expected 0x%x", header, HandshakeByte) - } - - if _, err := (*port).Write([]byte{AckByte}); err != nil { - *errorChan <- errors.New("could not write ACK") + if header[0] != shared.AckByte { + *errorChan <- fmt.Errorf("unexpected byte 0x%x expected 0x%x", header, shared.AckByte) return } - lengthBytes, err := shared.ReceiveBytes(port, 3, 2) - if err != nil { - *errorChan <- err - return - } - blockLength := binary.BigEndian.Uint32([]byte{0x0, lengthBytes[0], lengthBytes[1], lengthBytes[2]}) - blockData, err := shared.ReceiveBytes(port, int(blockLength), 5) - if err != nil { - *errorChan <- err - return - } - - checkSum, err := shared.ReceiveBytes(port, 1, 2) - if err != nil { - *errorChan <- err - return - } - log.Debugf("block checksum is %d", checkSum) - 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{AckByte}); err != nil { - *errorChan <- fmt.Errorf("could not write ACK byte: %v", err) - } - *responseChan <- blockData - - return -} - -func SendRequest(port serial.Port, request *datastructures.SignerRequest) (response *datastructures.SignerResponse, err error) { - log.Debugf("send request %v to serial port %v", request, port) - - if err = sendHandShake(port); err != nil { - return nil, err - } - - requestBytes := request.Serialize() - if length, err := port.Write(requestBytes); err != nil { - log.Fatal(err) - } else { - log.Debugf("wrote %d request bytes", length) - } - - if length, err := port.Write([]byte{datastructures.CalculateXorCheckSum([][]byte{requestBytes})}); err != nil { - log.Fatal(err) - } else { - log.Debugf("wrote %d checksum bytes", length) - } - - if length, err := port.Write([]byte(shared.MagicTrailer)); err != nil { - log.Fatal(err) - } else { - log.Debugf("wrote %d trailer bytes", length) - } - - header, err := shared.ReceiveBytes(&port, 1, 20) - if err != nil { - return nil, err - } - if header[0] != AckByte { - return nil, fmt.Errorf("unexpected byte 0x%x expected 0x%x", header, AckByte) - } - - responseChan := make(chan []byte, 1) - errChan := make(chan error, 1) - go receiveResponse(&port, &responseChan, &errChan) - - select { - case responseData := <-responseChan: - log.Debugf("response data: %v", responseData) - case err := <-errChan: - log.Errorf("%v", err) - } - - return nil, nil + receiveResponse(port, responseChan, errorChan) } diff --git a/datastructures/common.go b/datastructures/common.go index 6f29667..e3ccb63 100644 --- a/datastructures/common.go +++ b/datastructures/common.go @@ -6,6 +6,15 @@ type Action uint8 const ActionNul = Action(0) +func (a Action) String() string { + switch a { + case ActionNul: + return "NUL" + default: + return "unknown" + } +} + func encode24BitLength(data []byte) []byte { lengthBytes := make([]byte, 4) binary.BigEndian.PutUint32(lengthBytes, uint32(len(data))) @@ -13,7 +22,7 @@ func encode24BitLength(data []byte) []byte { } // calculate length from 24 bits of data in network byte order -func decode24BitLength(bytes []byte) int { +func Decode24BitLength(bytes []byte) int { return int(binary.BigEndian.Uint32([]byte{0x0, bytes[0], bytes[1], bytes[2]})) } diff --git a/datastructures/signerrequest.go b/datastructures/signerrequest.go index c15c461..90f5cb4 100644 --- a/datastructures/signerrequest.go +++ b/datastructures/signerrequest.go @@ -25,19 +25,19 @@ type SignerRequest struct { const protocolVersion = 1 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] contentBytes := blockData[3+headerLength:] - content1Length := decode24BitLength(contentBytes[0:3]) + content1Length := Decode24BitLength(contentBytes[0:3]) content1 := string(contentBytes[3 : 3+content1Length]) content2Offset := 3 + content1Length - content2Length := decode24BitLength(contentBytes[content2Offset : content2Offset+3]) + content2Length := Decode24BitLength(contentBytes[content2Offset : content2Offset+3]) content2 := string(contentBytes[3+content2Offset : 3+content2Offset+content2Length]) content3Offset := 3 + content2Offset + content2Length - content3Length := decode24BitLength(contentBytes[content3Offset : content3Offset+3]) + content3Length := Decode24BitLength(contentBytes[content3Offset : content3Offset+3]) content3 := string(contentBytes[3+content3Offset : 3+content3Offset+content3Length]) calculated := CalculateXorCheckSum([][]byte{lengthBytes, blockData}) @@ -79,8 +79,8 @@ func (r SignerRequest) Serialize() []byte { func NewNulRequest() *SignerRequest { return &SignerRequest{ - Version: protocolVersion, - Action: ActionNul, + Version: protocolVersion, + Action: ActionNul, Content1: time.Now().UTC().Format("010203042006.05"), } } diff --git a/datastructures/signerresponse.go b/datastructures/signerresponse.go index f6c36ba..9502e48 100644 --- a/datastructures/signerresponse.go +++ b/datastructures/signerresponse.go @@ -17,20 +17,26 @@ type SignerResponse struct { } func SignerResponseFromData(lengthBytes []byte, blockData []byte, checkSum byte) (*SignerResponse, error) { - headerLength := decode24BitLength(lengthBytes) - headerBytes := blockData[3 : 3+headerLength] + if len(blockData) < 3 { + return nil, errors.New("begin of structure corrupt") + } - contentBytes := blockData[3+headerLength:] - content1Length := decode24BitLength(contentBytes[0:3]) - content1 := string(contentBytes[3 : 3+content1Length]) + offset := 0 + headerLength := Decode24BitLength(blockData[offset : offset+3]) + offset += 3 + headerBytes := blockData[offset : offset+headerLength] + offset += headerLength - 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]) + content := make([]string, 3) + for offset < len(blockData) { + dataLength := Decode24BitLength(blockData[offset : offset+3]) + if len(blockData)-3 < dataLength { + return nil, errors.New("structure cut off") + } + offset += 3 + content = append(content, string(blockData[offset:offset+dataLength])) + offset += dataLength + } calculated := CalculateXorCheckSum([][]byte{lengthBytes, blockData}) if checkSum != calculated { @@ -42,9 +48,9 @@ func SignerResponseFromData(lengthBytes []byte, blockData []byte, checkSum byte) Action: Action(headerBytes[1]), Reserved1: headerBytes[2], Reserved2: headerBytes[3], - Content1: content1, - Content2: content2, - Content3: content3, + Content1: content[0], + Content2: content[1], + Content3: content[2], }, nil } diff --git a/shared/shared.go b/shared/shared.go index d0d6c12..1677f1c 100644 --- a/shared/shared.go +++ b/shared/shared.go @@ -1,3 +1,7 @@ package shared const MagicTrailer = "rie4Ech7" + +const HandshakeByte = 0x02 +const AckByte = 0x10 +const NackByte = 0x11 diff --git a/signer/main.go b/signer/main.go index 3e9e57d..56329b1 100644 --- a/signer/main.go +++ b/signer/main.go @@ -1,15 +1,15 @@ package main import ( - "encoding/binary" "errors" "flag" "fmt" - "git.cacert.org/cacert-gosigner/datastructures" - "git.cacert.org/cacert-gosigner/shared" "log" "time" + "git.cacert.org/cacert-gosigner/datastructures" + "git.cacert.org/cacert-gosigner/shared" + "github.com/goburrow/serial" ) @@ -96,7 +96,7 @@ func SendResponse(port *serial.Port, response *datastructures.SignerResponse) er } tryAgain := true - for ; tryAgain; { + for tryAgain { data := response.Serialize() if _, err := (*port).Write(data); err != nil { return err @@ -150,11 +150,11 @@ func Receive(port *serial.Port, commandChan *chan datastructures.SignerRequest, *errorChan <- err return } - if header[0] != 0x02 { - *errorChan <- fmt.Errorf("unexpected byte 0x%x expected 0x02", header) + if header[0] != shared.HandshakeByte { + *errorChan <- fmt.Errorf("unexpected byte 0x%x expected 0x%x", header[0], shared.HandshakeByte) } - if _, err := (*port).Write([]byte{0x10}); err != nil { + if _, err := (*port).Write([]byte{shared.AckByte}); err != nil { *errorChan <- errors.New("could not write ACK") return } @@ -164,8 +164,8 @@ func Receive(port *serial.Port, commandChan *chan datastructures.SignerRequest, *errorChan <- err return } - blockLength := binary.BigEndian.Uint32([]byte{0x0, lengthBytes[0], lengthBytes[1], lengthBytes[2]}) - blockData, err := shared.ReceiveBytes(port, int(blockLength), 5) + blockLength := datastructures.Decode24BitLength(lengthBytes) + blockData, err := shared.ReceiveBytes(port, blockLength, 5) if err != nil { *errorChan <- err return @@ -199,4 +199,3 @@ func Receive(port *serial.Port, commandChan *chan datastructures.SignerRequest, *commandChan <- *command } -