Configure golangci-lint and fix warnings
This commit is contained in:
parent
ecd1846975
commit
2e467b3d2e
20 changed files with 915 additions and 559 deletions
39
.golangci.yml
Normal file
39
.golangci.yml
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
---
|
||||||
|
output:
|
||||||
|
sort-results: true
|
||||||
|
|
||||||
|
linter-settings:
|
||||||
|
goimports:
|
||||||
|
local-prefixes: git.cacert.org/cacert-gosigner
|
||||||
|
misspell:
|
||||||
|
locale: US
|
||||||
|
ignore-words:
|
||||||
|
- CAcert
|
||||||
|
|
||||||
|
linters:
|
||||||
|
disable-all: false
|
||||||
|
enable:
|
||||||
|
- errorlint
|
||||||
|
- gocognit
|
||||||
|
- goconst
|
||||||
|
- gocritic
|
||||||
|
- gofmt
|
||||||
|
- goheader
|
||||||
|
- goimports
|
||||||
|
- golint
|
||||||
|
- gomnd
|
||||||
|
- gosec
|
||||||
|
- interfacer
|
||||||
|
- lll
|
||||||
|
- makezero
|
||||||
|
- misspell
|
||||||
|
- nakedret
|
||||||
|
- nestif
|
||||||
|
- nlreturn
|
||||||
|
- nolintlint
|
||||||
|
- predeclared
|
||||||
|
- rowserrcheck
|
||||||
|
- scopelint
|
||||||
|
- sqlclosecheck
|
||||||
|
- wrapcheck
|
||||||
|
- wsl
|
|
@ -2,10 +2,17 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/sirupsen/logrus"
|
"io/ioutil"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"go.bug.st/serial"
|
"go.bug.st/serial"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
"io/ioutil"
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dataBits = 8
|
||||||
|
defaultBaudRate = 115200
|
||||||
|
defaultBufferSize = 2048
|
||||||
)
|
)
|
||||||
|
|
||||||
type ClientConfig struct {
|
type ClientConfig struct {
|
||||||
|
@ -21,8 +28,8 @@ type ClientConfig struct {
|
||||||
|
|
||||||
var defaultConfig = ClientConfig{
|
var defaultConfig = ClientConfig{
|
||||||
SerialAddress: "/dev/ttyUSB0",
|
SerialAddress: "/dev/ttyUSB0",
|
||||||
BaudRate: 115200,
|
BaudRate: defaultBaudRate,
|
||||||
BufferSize: 2048,
|
BufferSize: defaultBufferSize,
|
||||||
Paranoid: false,
|
Paranoid: false,
|
||||||
Debug: false,
|
Debug: false,
|
||||||
OpenSSLBinary: "/usr/bin/openssl",
|
OpenSSLBinary: "/usr/bin/openssl",
|
||||||
|
@ -30,31 +37,36 @@ var defaultConfig = ClientConfig{
|
||||||
MySQLDSN: "<username>:<password>@/database?parseTime=true",
|
MySQLDSN: "<username>:<password>@/database?parseTime=true",
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateExampleConfig(configFile string) (config *ClientConfig, err error) {
|
func generateExampleConfig(configFile string) (*ClientConfig, error) {
|
||||||
config = &defaultConfig
|
config := &defaultConfig
|
||||||
|
|
||||||
configBytes, err := yaml.Marshal(config)
|
configBytes, err := yaml.Marshal(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("could not generate configuration data")
|
return nil, fmt.Errorf("could not generate configuration data: %w", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Infof("example data for %s:\n\n---\n%s\n", configFile, configBytes)
|
log.Infof("example data for %s:\n\n---\n%s\n", configFile, configBytes)
|
||||||
return
|
|
||||||
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readConfig(configFile string) (config *ClientConfig, err error) {
|
func readConfig(configFile string) (config *ClientConfig, err error) {
|
||||||
source, err := ioutil.ReadFile(configFile)
|
source, err := ioutil.ReadFile(configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("opening configuration file failed: %v", err)
|
log.Errorf("opening configuration file failed: %v", err)
|
||||||
if exampleConfig, err := generateExampleConfig(configFile); err != nil {
|
|
||||||
|
exampleConfig, err := generateExampleConfig(configFile)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
}
|
||||||
logrus.Info("starting with default config")
|
|
||||||
|
log.Info("starting with default config")
|
||||||
|
|
||||||
return exampleConfig, nil
|
return exampleConfig, nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if err := yaml.Unmarshal(source, &config); err != nil {
|
if err := yaml.Unmarshal(source, &config); err != nil {
|
||||||
return nil, fmt.Errorf("loading configuration file failed: %v", err)
|
return nil, fmt.Errorf("loading configuration file failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
|
@ -63,7 +75,7 @@ func readConfig(configFile string) (config *ClientConfig, err error) {
|
||||||
func fillSerialMode(clientConfig *ClientConfig) *serial.Mode {
|
func fillSerialMode(clientConfig *ClientConfig) *serial.Mode {
|
||||||
return &serial.Mode{
|
return &serial.Mode{
|
||||||
BaudRate: clientConfig.BaudRate,
|
BaudRate: clientConfig.BaudRate,
|
||||||
DataBits: 8,
|
DataBits: dataBits,
|
||||||
StopBits: serial.OneStopBit,
|
StopBits: serial.OneStopBit,
|
||||||
Parity: serial.NoParity,
|
Parity: serial.NoParity,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,15 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenerateExampleConfig(t *testing.T) {
|
func TestGenerateExampleConfig(t *testing.T) {
|
||||||
testOutput := bytes.Buffer{}
|
testOutput := bytes.Buffer{}
|
||||||
logrus.SetOutput(&testOutput)
|
log.SetOutput(&testOutput)
|
||||||
|
|
||||||
config, err := generateExampleConfig("test.yaml")
|
config, err := generateExampleConfig("test.yaml")
|
||||||
|
|
||||||
|
|
|
@ -16,29 +16,37 @@ import (
|
||||||
"git.cacert.org/cacert-gosigner/datastructures"
|
"git.cacert.org/cacert-gosigner/datastructures"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const mainLoopSleep = 2700
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var configFile string
|
var configFile string
|
||||||
|
|
||||||
flag.StringVar(&configFile, "c", "client.yaml", "client configuration file in YAML format")
|
flag.StringVar(&configFile, "c", "client.yaml", "client configuration file in YAML format")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
var clientConfig *ClientConfig
|
var (
|
||||||
var serialConfig *serial.Mode
|
clientConfig *ClientConfig
|
||||||
var err error
|
serialConfig *serial.Mode
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
if clientConfig, err = readConfig(configFile); err != nil {
|
if clientConfig, err = readConfig(configFile); err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
serialConfig = fillSerialMode(clientConfig)
|
serialConfig = fillSerialMode(clientConfig)
|
||||||
|
|
||||||
if clientConfig.Debug {
|
if clientConfig.Debug {
|
||||||
log.SetLevel(log.TraceLevel)
|
log.SetLevel(log.TraceLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("connecting to %s using %+v", clientConfig.SerialAddress, serialConfig)
|
log.Infof("connecting to %s using %+v", clientConfig.SerialAddress, serialConfig)
|
||||||
|
|
||||||
port, err := serial.Open(clientConfig.SerialAddress, serialConfig)
|
port, err := serial.Open(clientConfig.SerialAddress, serialConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("serial port connected")
|
log.Debug("serial port connected")
|
||||||
|
|
||||||
requestChannel := protocol.NewSignerProtocolRequestChannel()
|
requestChannel := protocol.NewSignerProtocolRequestChannel()
|
||||||
|
@ -48,6 +56,7 @@ func main() {
|
||||||
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, port, clientProtocolConfig,
|
requestChannel, &responseChannel, port, clientProtocolConfig,
|
||||||
)
|
)
|
||||||
|
@ -55,14 +64,17 @@ func main() {
|
||||||
cancelChannel := make(chan os.Signal, 1)
|
cancelChannel := make(chan os.Signal, 1)
|
||||||
signal.Notify(cancelChannel, syscall.SIGTERM, syscall.SIGINT)
|
signal.Notify(cancelChannel, syscall.SIGTERM, syscall.SIGINT)
|
||||||
|
|
||||||
|
const goRoutines = 2
|
||||||
|
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
wg.Add(2)
|
wg.Add(goRoutines)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := protocolHandler.HandleSignerProtocol(); err != nil {
|
if err := protocolHandler.HandleSignerProtocol(); err != nil {
|
||||||
log.Errorf("terminating because of %v", err)
|
log.Errorf("terminating because of %v", err)
|
||||||
close(cancelChannel)
|
close(cancelChannel)
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -75,20 +87,26 @@ func main() {
|
||||||
if sig != nil {
|
if sig != nil {
|
||||||
log.Infof("caught %+v", sig)
|
log.Infof("caught %+v", sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := protocolHandler.Close(); err != nil {
|
if err := protocolHandler.Close(); err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
} else {
|
} else {
|
||||||
log.Infof("protocol handler closed")
|
log.Infof("protocol handler closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := port.Close(); err != nil {
|
if err := port.Close(); err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
} else {
|
} else {
|
||||||
log.Infof("serial port closed")
|
log.Infof("serial port closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func runMainLoop(requestChannel *protocol.SignerProtocolRequestChannel, responseChannel *chan *datastructures.SignerResponse) {
|
func runMainLoop(
|
||||||
|
requestChannel *protocol.SignerProtocolRequestChannel,
|
||||||
|
responseChannel *chan *datastructures.SignerResponse,
|
||||||
|
) {
|
||||||
crlCheck := 0
|
crlCheck := 0
|
||||||
|
|
||||||
log.Debug("starting main loop")
|
log.Debug("starting main loop")
|
||||||
|
@ -99,30 +117,28 @@ func runMainLoop(requestChannel *protocol.SignerProtocolRequestChannel, response
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace("processing goroutine terminated")
|
log.Trace("processing goroutine terminated")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
log.Debug("handling GPG database ...")
|
log.Debug("handling GPG database ...") // HandleGPG(&requestChannel)
|
||||||
// HandleGPG(&requestChannel)
|
log.Debug("issuing certificates ...") // HandleCertificates(&requestChannel)
|
||||||
log.Debug("issuing certificates ...")
|
log.Debug("revoking certificates ...") // RevokeCertificates(&requestChannel)
|
||||||
// HandleCertificates(&requestChannel)
|
|
||||||
log.Debug("revoking certificates ...")
|
|
||||||
// RevokeCertificates(&requestChannel)
|
|
||||||
|
|
||||||
crlCheck++
|
crlCheck++
|
||||||
if crlCheck%100 == 0 {
|
if crlCheck%100 == 0 {
|
||||||
log.Debug("refresh CRLs ...")
|
log.Debug("refresh CRLs ...") // RefreshCRLs(&requestChannel)
|
||||||
// RefreshCRLs(&requestChannel)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if requestChannel.IsClosed() {
|
if requestChannel.IsClosed() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("send NUL request to keep connection open")
|
log.Debug("send NUL request to keep connection open")
|
||||||
requestChannel.C <- datastructures.NewNulRequest()
|
requestChannel.C <- datastructures.NewNulRequest()
|
||||||
|
|
||||||
log.Debug("sleep for 2.7 seconds")
|
log.Debug("sleep for 2.7 seconds")
|
||||||
time.Sleep(2700 * time.Millisecond)
|
time.Sleep(mainLoopSleep * time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ func Process(response *datastructures.SignerResponse) (err error) {
|
||||||
switch response.Action {
|
switch response.Action {
|
||||||
case shared.ActionNul:
|
case shared.ActionNul:
|
||||||
logrus.Trace("received response for NUL request")
|
logrus.Trace("received response for NUL request")
|
||||||
|
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported action in response 0x%x", response.Action)
|
return fmt.Errorf("unsupported action in response 0x%x", response.Action)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package protocol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -13,17 +14,24 @@ import (
|
||||||
"git.cacert.org/cacert-gosigner/shared"
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
waitForHeader = 20
|
||||||
|
waitForData = 5
|
||||||
|
waitForHandShake = 120
|
||||||
|
bufferSize = 2048
|
||||||
|
)
|
||||||
|
|
||||||
type SignerProtocolHandler interface {
|
type SignerProtocolHandler interface {
|
||||||
io.Closer
|
io.Closer
|
||||||
HandleSignerProtocol() error
|
HandleSignerProtocol() error
|
||||||
}
|
}
|
||||||
|
|
||||||
type signerProtocolConfig struct {
|
type SignerProtocolConfig struct {
|
||||||
BufferSize int
|
BufferSize int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSignerProtocolConfig() *signerProtocolConfig {
|
func NewSignerProtocolConfig() *SignerProtocolConfig {
|
||||||
return &signerProtocolConfig{BufferSize: 2048}
|
return &SignerProtocolConfig{BufferSize: bufferSize}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SignerProtocolRequestChannel struct {
|
type SignerProtocolRequestChannel struct {
|
||||||
|
@ -39,6 +47,7 @@ func NewSignerProtocolRequestChannel() *SignerProtocolRequestChannel {
|
||||||
func (rc *SignerProtocolRequestChannel) SafeClose() {
|
func (rc *SignerProtocolRequestChannel) SafeClose() {
|
||||||
rc.mutex.Lock()
|
rc.mutex.Lock()
|
||||||
defer rc.mutex.Unlock()
|
defer rc.mutex.Unlock()
|
||||||
|
|
||||||
if !rc.closed {
|
if !rc.closed {
|
||||||
close(rc.C)
|
close(rc.C)
|
||||||
rc.closed = true
|
rc.closed = true
|
||||||
|
@ -48,6 +57,7 @@ func (rc *SignerProtocolRequestChannel) SafeClose() {
|
||||||
func (rc *SignerProtocolRequestChannel) IsClosed() bool {
|
func (rc *SignerProtocolRequestChannel) IsClosed() bool {
|
||||||
rc.mutex.Lock()
|
rc.mutex.Lock()
|
||||||
defer rc.mutex.Unlock()
|
defer rc.mutex.Unlock()
|
||||||
|
|
||||||
return rc.closed
|
return rc.closed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +65,7 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
type UnExpectedAcknowledgeByte struct {
|
type UnExpectedAcknowledgeByte struct {
|
||||||
|
@ -75,6 +85,7 @@ func (e UnExpectedAcknowledgeByte) Error() string {
|
||||||
func (ph *protocolHandler) Close() error {
|
func (ph *protocolHandler) Close() error {
|
||||||
close(*ph.responseChannel)
|
close(*ph.responseChannel)
|
||||||
ph.requestChannel.SafeClose()
|
ph.requestChannel.SafeClose()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +93,7 @@ func NewProtocolHandler(
|
||||||
requests *SignerProtocolRequestChannel,
|
requests *SignerProtocolRequestChannel,
|
||||||
response *chan *datastructures.SignerResponse,
|
response *chan *datastructures.SignerResponse,
|
||||||
serialConnection io.ReadWriteCloser,
|
serialConnection io.ReadWriteCloser,
|
||||||
config *signerProtocolConfig,
|
config *SignerProtocolConfig,
|
||||||
) SignerProtocolHandler {
|
) SignerProtocolHandler {
|
||||||
return &protocolHandler{
|
return &protocolHandler{
|
||||||
requestChannel: requests,
|
requestChannel: requests,
|
||||||
|
@ -93,85 +104,115 @@ func NewProtocolHandler(
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ph *protocolHandler) HandleSignerProtocol() error {
|
func (ph *protocolHandler) HandleSignerProtocol() error {
|
||||||
for {
|
for request := range ph.requestChannel.C {
|
||||||
select {
|
|
||||||
case request := <-ph.requestChannel.C:
|
|
||||||
log.Tracef("handle request %+v", request)
|
log.Tracef("handle request %+v", request)
|
||||||
var err error
|
|
||||||
var lengthBytes, responseBytes *[]byte
|
var (
|
||||||
var checksum byte
|
err error
|
||||||
|
lengthBytes, responseBytes *[]byte
|
||||||
|
checksum byte
|
||||||
|
)
|
||||||
|
|
||||||
if err = ph.sendHandshake(); err != nil {
|
if err = ph.sendHandshake(); err != nil {
|
||||||
switch err.(type) {
|
var e *UnExpectedHandshakeByte
|
||||||
case UnExpectedAcknowledgeByte:
|
if errors.As(err, &e) {
|
||||||
log.Errorf("unexpected handshake byte: 0x%x", err.(UnExpectedAcknowledgeByte).ResponseByte)
|
log.Errorf("unexpected handshake byte: 0x%x", e.ResponseByte)
|
||||||
// TODO drain input
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
requestBytes := request.Serialize()
|
requestBytes := request.Serialize()
|
||||||
|
|
||||||
if err = ph.sendRequest(&requestBytes); err != nil {
|
if err = ph.sendRequest(&requestBytes); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = ph.waitForResponseHandshake(); err != nil {
|
if err = ph.waitForResponseHandshake(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if lengthBytes, responseBytes, checksum, err = ph.readResponse(); err != nil {
|
if lengthBytes, responseBytes, checksum, err = ph.readResponse(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := datastructures.SignerResponseFromData(*lengthBytes, *responseBytes, checksum)
|
response, err := datastructures.SignerResponseFromData(*lengthBytes, *responseBytes, checksum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("could not create response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
*ph.responseChannel <- response
|
*ph.responseChannel <- response
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ph *protocolHandler) sendHandshake() (err error) {
|
func (ph *protocolHandler) sendHandshake() error {
|
||||||
var bytesWritten, bytesRead int
|
var (
|
||||||
if bytesWritten, err = ph.serialConnection.Write([]byte{shared.HandshakeByte}); err != nil {
|
bytesWritten, bytesRead int
|
||||||
return
|
err error
|
||||||
} else {
|
)
|
||||||
|
|
||||||
|
data := make([]byte, 0)
|
||||||
|
|
||||||
|
bytesWritten, err = ph.serialConnection.Write([]byte{shared.HandshakeByte})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not send handshake byte: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
log.Tracef("wrote %d bytes of handshake info", bytesWritten)
|
log.Tracef("wrote %d bytes of handshake info", bytesWritten)
|
||||||
|
|
||||||
|
bytesRead, err = ph.serialConnection.Read(data)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not receieve ACK byte: %w", err)
|
||||||
}
|
}
|
||||||
data := make([]byte, 1)
|
|
||||||
if bytesRead, err = ph.serialConnection.Read(data); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Tracef("%d bytes read", bytesRead)
|
log.Tracef("%d bytes read", bytesRead)
|
||||||
|
|
||||||
if bytesRead != 1 || data[0] != shared.AckByte {
|
if bytesRead != 1 || data[0] != shared.AckByte {
|
||||||
log.Warnf("received invalid handshake byte 0x%x", data[0])
|
log.Warnf("received invalid handshake byte 0x%x", data[0])
|
||||||
|
|
||||||
return UnExpectedAcknowledgeByte{data[0]}
|
return UnExpectedAcknowledgeByte{data[0]}
|
||||||
}
|
}
|
||||||
return
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ph *protocolHandler) sendRequest(requestBytes *[]byte) error {
|
func (ph *protocolHandler) sendRequest(requestBytes *[]byte) error {
|
||||||
|
var (
|
||||||
|
n int
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if length, err := ph.serialConnection.Write(*requestBytes); err != nil {
|
n, err = ph.serialConnection.Write(*requestBytes)
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
log.Tracef("wrote %d request bytes", length)
|
|
||||||
}
|
|
||||||
|
|
||||||
if length, err := ph.serialConnection.Write([]byte{
|
|
||||||
datastructures.CalculateXorCheckSum([][]byte{*requestBytes}),
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
log.Tracef("wrote %d checksum bytes", length)
|
|
||||||
}
|
|
||||||
|
|
||||||
if length, err := ph.serialConnection.Write([]byte(shared.MagicTrailer)); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
log.Tracef("wrote %d trailer bytes", length)
|
|
||||||
}
|
|
||||||
header, err := shared.ReceiveBytes(ph.serialConnection, 1, 20*time.Second)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("could not send request bytes: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Tracef("wrote %d request bytes", n)
|
||||||
|
|
||||||
|
n, err = ph.serialConnection.Write([]byte{
|
||||||
|
datastructures.CalculateXorCheckSum([][]byte{*requestBytes}),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not send checksum byte: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Tracef("wrote %d checksum bytes", n)
|
||||||
|
|
||||||
|
n, err = ph.serialConnection.Write([]byte(shared.MagicTrailer))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not send trailer bytes: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Tracef("wrote %d trailer bytes", n)
|
||||||
|
|
||||||
|
header, err := shared.ReceiveBytes(ph.serialConnection, 1, waitForHeader*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not read header bytes: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
switch header[0] {
|
switch header[0] {
|
||||||
case shared.AckByte:
|
case shared.AckByte:
|
||||||
return nil
|
return nil
|
||||||
|
@ -182,62 +223,78 @@ func (ph *protocolHandler) sendRequest(requestBytes *[]byte) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ph *protocolHandler) waitForResponseHandshake() (err error) {
|
func (ph *protocolHandler) waitForResponseHandshake() error {
|
||||||
data, err := shared.ReceiveBytes(ph.serialConnection, 1, 120*time.Second)
|
data, err := shared.ReceiveBytes(ph.serialConnection, 1, waitForHandShake*time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("could not receive handshake byte: %w", err)
|
||||||
}
|
|
||||||
if len(data) != 1 || data[0] != shared.HandshakeByte {
|
|
||||||
log.Warnf("received invalid handshake byte 0x%x", data[0])
|
|
||||||
return UnExpectedHandshakeByte{data[0]}
|
|
||||||
}
|
|
||||||
if err = shared.SendBytes(ph.serialConnection, []byte{shared.AckByte}); err != nil {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
if len(data) != 1 || data[0] != shared.HandshakeByte {
|
||||||
|
log.Warnf("received invalid handshake byte 0x%x", data[0])
|
||||||
|
|
||||||
|
return UnExpectedHandshakeByte{data[0]}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = shared.SendBytes(ph.serialConnection, []byte{shared.AckByte}); err != nil {
|
||||||
|
return fmt.Errorf("could not send ACK: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ph *protocolHandler) readResponse() (*[]byte, *[]byte, byte, error) {
|
func (ph *protocolHandler) readResponse() (*[]byte, *[]byte, byte, error) {
|
||||||
dataLength := -1
|
dataLength := -1
|
||||||
var lengthBuffer = bytes.NewBuffer(make([]byte, 0))
|
|
||||||
var byteBuffer = bytes.NewBuffer(make([]byte, 0))
|
var (
|
||||||
|
lengthBuffer = bytes.NewBuffer(make([]byte, 0))
|
||||||
|
byteBuffer = bytes.NewBuffer(make([]byte, 0))
|
||||||
|
)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
readBuffer, err := shared.ReceiveBytes(ph.serialConnection, ph.config.BufferSize, 5*time.Second)
|
readBuffer, err := shared.ReceiveBytes(ph.serialConnection, ph.config.BufferSize, waitForData*time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, err
|
return nil, nil, 0, fmt.Errorf("could not receive response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bytesRead := len(readBuffer)
|
bytesRead := len(readBuffer)
|
||||||
if bytesRead > 0 {
|
if bytesRead > 0 {
|
||||||
byteBuffer.Write(readBuffer[0:bytesRead])
|
byteBuffer.Write(readBuffer[0:bytesRead])
|
||||||
|
|
||||||
for _, b := range readBuffer {
|
for _, b := range readBuffer {
|
||||||
if lengthBuffer.Len() < 3 {
|
if lengthBuffer.Len() < shared.LengthFieldSize {
|
||||||
lengthBuffer.WriteByte(b)
|
lengthBuffer.WriteByte(b)
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if dataLength < 0 && lengthBuffer.Len() == 3 {
|
|
||||||
|
if dataLength < 0 && lengthBuffer.Len() == shared.LengthFieldSize {
|
||||||
dataLength = datastructures.Decode24BitLength(lengthBuffer.Bytes())
|
dataLength = datastructures.Decode24BitLength(lengthBuffer.Bytes())
|
||||||
log.Tracef("expect to read %d data bytes", dataLength)
|
log.Tracef("expect to read %d data bytes", dataLength)
|
||||||
}
|
}
|
||||||
if dataLength == byteBuffer.Len()-4-len(shared.MagicTrailer) {
|
|
||||||
|
trailerOffset := shared.LengthFieldSize + shared.CheckSumFieldSize
|
||||||
|
|
||||||
|
if dataLength == byteBuffer.Len()-trailerOffset-len(shared.MagicTrailer) {
|
||||||
allBytes := byteBuffer.Bytes()
|
allBytes := byteBuffer.Bytes()
|
||||||
trailer := string(allBytes[4+dataLength:])
|
|
||||||
|
trailer := string(allBytes[trailerOffset+dataLength:])
|
||||||
if trailer != shared.MagicTrailer {
|
if trailer != shared.MagicTrailer {
|
||||||
return nil, nil, 0, fmt.Errorf("invalid trailer bytes: %v", trailer)
|
return nil, nil, 0, fmt.Errorf("invalid trailer bytes: %v", trailer)
|
||||||
}
|
}
|
||||||
lengthBytes := allBytes[0:3]
|
|
||||||
dataBytes := allBytes[3 : 3+dataLength]
|
lengthBytes := allBytes[0:shared.LengthFieldSize]
|
||||||
checkSum := allBytes[3+dataLength]
|
dataBytes := allBytes[shared.LengthFieldSize : shared.LengthFieldSize+dataLength]
|
||||||
|
checkSum := allBytes[shared.LengthFieldSize+dataLength]
|
||||||
|
|
||||||
calculatedChecksum := datastructures.CalculateXorCheckSum([][]byte{lengthBytes, dataBytes})
|
calculatedChecksum := datastructures.CalculateXorCheckSum([][]byte{lengthBytes, dataBytes})
|
||||||
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.SendBytes(ph.serialConnection, []byte{shared.AckByte}); err != nil {
|
if err := shared.SendBytes(ph.serialConnection, []byte{shared.AckByte}); err != nil {
|
||||||
return nil, nil, 0, err
|
return nil, nil, 0, fmt.Errorf("could not send ACK byte: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &lengthBytes, &dataBytes, checkSum, nil
|
return &lengthBytes, &dataBytes, checkSum, nil
|
||||||
|
|
|
@ -14,11 +14,12 @@ import (
|
||||||
func main() {
|
func main() {
|
||||||
var (
|
var (
|
||||||
address string
|
address string
|
||||||
baudRate int
|
baudRate, dataBits int
|
||||||
)
|
)
|
||||||
|
|
||||||
flag.StringVar(&address, "a", "/dev/ttyUSB0", "address")
|
flag.StringVar(&address, "a", "/dev/ttyUSB0", "address")
|
||||||
flag.IntVar(&baudRate, "b", 115200, "baud rate")
|
flag.IntVar(&baudRate, "b", 115200, "baud rate")
|
||||||
|
flag.IntVar(&dataBits, "d", 8, "data bits")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
log.SetFormatter(&log.TextFormatter{
|
log.SetFormatter(&log.TextFormatter{
|
||||||
|
@ -29,15 +30,18 @@ func main() {
|
||||||
|
|
||||||
serialMode := &serial.Mode{
|
serialMode := &serial.Mode{
|
||||||
BaudRate: baudRate,
|
BaudRate: baudRate,
|
||||||
DataBits: 8,
|
DataBits: dataBits,
|
||||||
StopBits: serial.OneStopBit,
|
StopBits: serial.OneStopBit,
|
||||||
Parity: serial.NoParity,
|
Parity: serial.NoParity,
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("connecting to %s using %+v", address, serialMode)
|
log.Infof("connecting to %s using %+v", address, serialMode)
|
||||||
|
|
||||||
port, err := serial.Open(address, serialMode)
|
port, err := serial.Open(address, serialMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("could not open serial port: %v", err)
|
log.Fatalf("could not open serial port: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("connected")
|
log.Info("connected")
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -45,6 +49,7 @@ func main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("could not close port: %v", err)
|
log.Fatalf("could not close port: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("serial port closed")
|
log.Info("serial port closed")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ const signerTimeFormat = "010203042006.05"
|
||||||
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)))
|
||||||
|
|
||||||
return lengthBytes[1:]
|
return lengthBytes[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,10 +18,12 @@ func Decode24BitLength(bytes []byte) int {
|
||||||
|
|
||||||
func CalculateXorCheckSum(byteBlocks [][]byte) byte {
|
func CalculateXorCheckSum(byteBlocks [][]byte) byte {
|
||||||
var result byte = 0x0
|
var result byte = 0x0
|
||||||
|
|
||||||
for _, byteBlock := range byteBlocks {
|
for _, byteBlock := range byteBlocks {
|
||||||
for _, b := range byteBlock {
|
for _, b := range byteBlock {
|
||||||
result ^= b
|
result ^= b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,21 @@ import (
|
||||||
"git.cacert.org/cacert-gosigner/shared"
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
headerPosSystem = 2
|
||||||
|
headerPosRoot = 3
|
||||||
|
headerPosProfile = 4
|
||||||
|
headerPosSignatureAlgorithm = 5
|
||||||
|
headerPosDay = 6
|
||||||
|
)
|
||||||
|
|
||||||
type SignerRequest struct {
|
type SignerRequest struct {
|
||||||
Version uint8
|
Version uint8
|
||||||
Action shared.Action
|
Action shared.Action
|
||||||
System shared.CryptoSystemId
|
System shared.CryptoSystemID
|
||||||
Root shared.CryptoSystemRootId
|
Root shared.CryptoSystemRootID
|
||||||
Profile shared.CertificateProfileId
|
Profile shared.CertificateProfileID
|
||||||
MdAlgorithm shared.MessageDigestAlgorithmId
|
MdAlgorithm shared.SignatureAlgorithmID
|
||||||
Days uint16
|
Days uint16
|
||||||
Spkac uint8
|
Spkac uint8
|
||||||
Content1 []byte
|
Content1 []byte
|
||||||
|
@ -24,33 +32,33 @@ type SignerRequest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func SignerRequestFromData(blockData []byte) (*SignerRequest, error) {
|
func SignerRequestFromData(blockData []byte) (*SignerRequest, error) {
|
||||||
headerLength := Decode24BitLength(blockData[0:3])
|
headerLength := Decode24BitLength(blockData[0:shared.LengthFieldSize])
|
||||||
headerBytes := blockData[3 : 3+headerLength]
|
headerBytes := blockData[shared.LengthFieldSize : shared.LengthFieldSize+headerLength]
|
||||||
|
|
||||||
contentBytes := blockData[3+headerLength:]
|
contentBytes := blockData[shared.LengthFieldSize+headerLength:]
|
||||||
content1Length := Decode24BitLength(contentBytes[0:3])
|
contentLen := Decode24BitLength(contentBytes[0:shared.LengthFieldSize])
|
||||||
content1 := contentBytes[3 : 3+content1Length]
|
content := contentBytes[shared.LengthFieldSize : shared.LengthFieldSize+contentLen]
|
||||||
|
|
||||||
content2Offset := 3 + content1Length
|
argument1Offset := shared.LengthFieldSize + contentLen
|
||||||
content2Length := Decode24BitLength(contentBytes[content2Offset : content2Offset+3])
|
argument1Len := Decode24BitLength(contentBytes[argument1Offset : argument1Offset+shared.LengthFieldSize])
|
||||||
content2 := contentBytes[3+content2Offset : 3+content2Offset+content2Length]
|
argument1 := contentBytes[shared.LengthFieldSize+argument1Offset : shared.LengthFieldSize+argument1Offset+argument1Len]
|
||||||
|
|
||||||
content3Offset := 3 + content2Offset + content2Length
|
argument2Offset := shared.LengthFieldSize + argument1Offset + argument1Len
|
||||||
content3Length := Decode24BitLength(contentBytes[content3Offset : content3Offset+3])
|
argument2Len := Decode24BitLength(contentBytes[argument2Offset : argument2Offset+shared.LengthFieldSize])
|
||||||
content3 := contentBytes[3+content3Offset : 3+content3Offset+content3Length]
|
argument2 := contentBytes[shared.LengthFieldSize+argument2Offset : shared.LengthFieldSize+argument2Offset+argument2Len]
|
||||||
|
|
||||||
return &SignerRequest{
|
return &SignerRequest{
|
||||||
Version: headerBytes[0],
|
Version: headerBytes[headerPosVersion],
|
||||||
Action: shared.Action(headerBytes[1]),
|
Action: shared.Action(headerBytes[headerPosAction]),
|
||||||
System: shared.CryptoSystemId(headerBytes[2]),
|
System: shared.CryptoSystemID(headerBytes[headerPosSystem]),
|
||||||
Root: shared.CryptoSystemRootId(headerBytes[3]),
|
Root: shared.CryptoSystemRootID(headerBytes[headerPosRoot]),
|
||||||
Profile: shared.CertificateProfileId(headerBytes[4]),
|
Profile: shared.CertificateProfileID(headerBytes[headerPosProfile]),
|
||||||
MdAlgorithm: shared.MessageDigestAlgorithmId(headerBytes[5]),
|
MdAlgorithm: shared.SignatureAlgorithmID(headerBytes[headerPosSignatureAlgorithm]),
|
||||||
Days: binary.BigEndian.Uint16([]byte{headerBytes[6], headerBytes[7]}),
|
Days: binary.BigEndian.Uint16(headerBytes[headerPosDay : headerPosDay+1]),
|
||||||
Spkac: headerBytes[8],
|
Spkac: headerBytes[8],
|
||||||
Content1: content1,
|
Content1: content,
|
||||||
Content2: content2,
|
Content2: argument1,
|
||||||
Content3: content3,
|
Content3: argument2,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +84,7 @@ func (r *SignerRequest) Serialize() []byte {
|
||||||
Encode24BitLength(content2Bytes), content2Bytes,
|
Encode24BitLength(content2Bytes), content2Bytes,
|
||||||
Encode24BitLength(content3Bytes), content3Bytes,
|
Encode24BitLength(content3Bytes), content3Bytes,
|
||||||
}, []byte{})
|
}, []byte{})
|
||||||
|
|
||||||
return bytes.Join([][]byte{Encode24BitLength(blockBytes), blockBytes}, []byte{})
|
return bytes.Join([][]byte{Encode24BitLength(blockBytes), blockBytes}, []byte{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,9 +106,11 @@ func (r *SignerRequest) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func shorten(original []byte) []byte {
|
func shorten(original []byte) []byte {
|
||||||
if len(original) > 20 {
|
const maxLength = 20
|
||||||
return original[:20]
|
if len(original) > maxLength {
|
||||||
|
return original[:maxLength]
|
||||||
}
|
}
|
||||||
|
|
||||||
return original
|
return original
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,16 @@ import (
|
||||||
"git.cacert.org/cacert-gosigner/shared"
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
headerPosVersion = 0
|
||||||
|
headerPosAction = 1
|
||||||
|
headerPosReserved1 = 2
|
||||||
|
headerPosReserved2 = 3
|
||||||
|
blockPosContent = 0
|
||||||
|
blockPosArgument1 = 1
|
||||||
|
blockPosArgument2 = 2
|
||||||
|
)
|
||||||
|
|
||||||
type SignerResponse struct {
|
type SignerResponse struct {
|
||||||
Version uint8
|
Version uint8
|
||||||
Action shared.Action
|
Action shared.Action
|
||||||
|
@ -19,23 +29,25 @@ type SignerResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func SignerResponseFromData(lengthBytes []byte, blockData []byte, checkSum byte) (*SignerResponse, error) {
|
func SignerResponseFromData(lengthBytes []byte, blockData []byte, checkSum byte) (*SignerResponse, error) {
|
||||||
if len(blockData) < 3 {
|
if len(blockData) < shared.LengthFieldSize {
|
||||||
return nil, errors.New("begin of structure corrupt")
|
return nil, errors.New("begin of structure corrupt")
|
||||||
}
|
}
|
||||||
|
|
||||||
offset := 0
|
offset := 0
|
||||||
headerLength := Decode24BitLength(blockData[offset : offset+3])
|
headerLength := Decode24BitLength(blockData[offset : offset+shared.LengthFieldSize])
|
||||||
offset += 3
|
offset += shared.LengthFieldSize
|
||||||
headerBytes := blockData[offset : offset+headerLength]
|
headerBytes := blockData[offset : offset+headerLength]
|
||||||
offset += headerLength
|
offset += headerLength
|
||||||
|
|
||||||
content := make([][]byte, 3)
|
content := make([][]byte, 0)
|
||||||
|
|
||||||
for offset < len(blockData) {
|
for offset < len(blockData) {
|
||||||
dataLength := Decode24BitLength(blockData[offset : offset+3])
|
dataLength := Decode24BitLength(blockData[offset : offset+3])
|
||||||
if len(blockData)-3 < dataLength {
|
if len(blockData)-shared.LengthFieldSize < dataLength {
|
||||||
return nil, errors.New("structure cut off")
|
return nil, errors.New("structure cut off")
|
||||||
}
|
}
|
||||||
offset += 3
|
|
||||||
|
offset += shared.LengthFieldSize
|
||||||
content = append(content, blockData[offset:offset+dataLength])
|
content = append(content, blockData[offset:offset+dataLength])
|
||||||
offset += dataLength
|
offset += dataLength
|
||||||
}
|
}
|
||||||
|
@ -46,13 +58,13 @@ func SignerResponseFromData(lengthBytes []byte, blockData []byte, checkSum byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SignerResponse{
|
return &SignerResponse{
|
||||||
Version: headerBytes[0],
|
Version: headerBytes[headerPosVersion],
|
||||||
Action: shared.Action(headerBytes[1]),
|
Action: shared.Action(headerBytes[headerPosAction]),
|
||||||
Reserved1: headerBytes[2],
|
Reserved1: headerBytes[headerPosReserved1],
|
||||||
Reserved2: headerBytes[3],
|
Reserved2: headerBytes[headerPosReserved2],
|
||||||
Content: content[0],
|
Content: content[blockPosContent],
|
||||||
Argument1: content[1],
|
Argument1: content[blockPosArgument1],
|
||||||
Argument2: content[2],
|
Argument2: content[blockPosArgument2],
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +76,7 @@ func (r SignerResponse) Serialize() []byte {
|
||||||
Encode24BitLength(r.Argument1), r.Argument1,
|
Encode24BitLength(r.Argument1), r.Argument1,
|
||||||
Encode24BitLength(r.Argument2), r.Argument2,
|
Encode24BitLength(r.Argument2), r.Argument2,
|
||||||
}, []byte{})
|
}, []byte{})
|
||||||
|
|
||||||
return bytes.Join([][]byte{Encode24BitLength(blockBytes), blockBytes}, []byte{})
|
return bytes.Join([][]byte{Encode24BitLength(blockBytes), blockBytes}, []byte{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
shared/io.go
17
shared/io.go
|
@ -13,12 +13,15 @@ import (
|
||||||
func ReceiveBytes(port io.Reader, 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() {
|
||||||
buffer := bytes.NewBuffer([]byte{})
|
buffer := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
for remainder := count; remainder > 0; {
|
for remainder := count; remainder > 0; {
|
||||||
data := make([]byte, remainder)
|
data := make([]byte, remainder)
|
||||||
if readBytes, err := port.Read(data); err != nil {
|
if readBytes, err := port.Read(data); err != nil {
|
||||||
errCh <- err
|
errCh <- err
|
||||||
|
|
||||||
return
|
return
|
||||||
} else if readBytes > 0 {
|
} else if readBytes > 0 {
|
||||||
buffer.Write(data[0:readBytes])
|
buffer.Write(data[0:readBytes])
|
||||||
|
@ -26,6 +29,7 @@ func ReceiveBytes(port io.Reader, count int, timeout time.Duration) ([]byte, err
|
||||||
log.Tracef("%d bytes read, remaining %d", readBytes, remainder)
|
log.Tracef("%d bytes read, remaining %d", readBytes, remainder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readCh <- buffer.Bytes()
|
readCh <- buffer.Bytes()
|
||||||
close(readCh)
|
close(readCh)
|
||||||
}()
|
}()
|
||||||
|
@ -38,19 +42,24 @@ func ReceiveBytes(port io.Reader, count int, timeout time.Duration) ([]byte, err
|
||||||
return nil, err
|
return nil, err
|
||||||
case data := <-readCh:
|
case data := <-readCh:
|
||||||
log.Tracef("received %d bytes from channel", len(data))
|
log.Tracef("received %d bytes from channel", len(data))
|
||||||
|
|
||||||
if data == nil {
|
if data == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.Write(data)
|
buffer.Write(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer.Bytes(), nil
|
return buffer.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendBytes(port io.Writer, data []byte) error {
|
func SendBytes(port io.Writer, data []byte) error {
|
||||||
if bytesWritten, err := port.Write(data); err != nil {
|
n, err := port.Write(data)
|
||||||
return err
|
if err != nil {
|
||||||
} else {
|
return fmt.Errorf("could not send bytes: %w", err)
|
||||||
log.Tracef("wrote %d bytes", bytesWritten)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Tracef("wrote %d bytes", n)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ type Action byte
|
||||||
const (
|
const (
|
||||||
ActionNul = Action(0)
|
ActionNul = Action(0)
|
||||||
ActionSign = Action(1)
|
ActionSign = Action(1)
|
||||||
ActionRevoke = Action(2)
|
ActionRevoke = Action(2) // nolint:gomnd
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a Action) String() string {
|
func (a Action) String() string {
|
||||||
|
@ -35,10 +35,10 @@ func (a Action) String() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type CryptoSystemRootId byte
|
type CryptoSystemRootID byte
|
||||||
|
|
||||||
type CertificateProfileId byte
|
type CertificateProfileID byte
|
||||||
|
|
||||||
type MessageDigestAlgorithmId byte
|
type SignatureAlgorithmID byte
|
||||||
|
|
||||||
type CryptoSystemId byte
|
type CryptoSystemID byte
|
||||||
|
|
|
@ -19,8 +19,8 @@ import (
|
||||||
|
|
||||||
"git.cacert.org/cacert-gosigner/datastructures"
|
"git.cacert.org/cacert-gosigner/datastructures"
|
||||||
"git.cacert.org/cacert-gosigner/shared"
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
"git.cacert.org/cacert-gosigner/signer/openpgp_ops"
|
"git.cacert.org/cacert-gosigner/signer/openpgpops"
|
||||||
"git.cacert.org/cacert-gosigner/signer/x509_ops"
|
"git.cacert.org/cacert-gosigner/signer/x509ops"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommandProcessorSettings struct {
|
type CommandProcessorSettings struct {
|
||||||
|
@ -34,7 +34,7 @@ type CommandProcessorSettings struct {
|
||||||
// functionality.
|
// functionality.
|
||||||
type CommandProcessor struct {
|
type CommandProcessor struct {
|
||||||
Settings *CommandProcessorSettings
|
Settings *CommandProcessorSettings
|
||||||
CryptoSystems map[shared.CryptoSystemId]*CryptoSystem
|
CryptoSystems map[shared.CryptoSystemID]*CryptoSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the signer request
|
// Process the signer request
|
||||||
|
@ -56,11 +56,11 @@ func (p *CommandProcessor) Process(command *datastructures.SignerRequest) (
|
||||||
case shared.ActionRevoke:
|
case shared.ActionRevoke:
|
||||||
return p.handleRevokeAction(command)
|
return p.handleRevokeAction(command)
|
||||||
default:
|
default:
|
||||||
return nil, errors.New(fmt.Sprintf(
|
return nil, fmt.Errorf(
|
||||||
"unsupported Action 0x%02x %s",
|
"unsupported Action 0x%02x %s",
|
||||||
int(command.Action),
|
int(command.Action),
|
||||||
command.Action,
|
command.Action,
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,10 +69,12 @@ func (*CommandProcessor) handleNulAction(command *datastructures.SignerRequest)
|
||||||
error,
|
error,
|
||||||
) {
|
) {
|
||||||
var timeSpec unix.Timespec
|
var timeSpec unix.Timespec
|
||||||
|
|
||||||
err := unix.ClockGettime(unix.CLOCK_REALTIME, &timeSpec)
|
err := unix.ClockGettime(unix.CLOCK_REALTIME, &timeSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("could not get system time: %v", err)
|
log.Errorf("could not get system time: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("current system time is %v", timeSpec)
|
log.Debugf("current system time is %v", timeSpec)
|
||||||
// TODO: calculate the actual system time from the payload
|
// TODO: calculate the actual system time from the payload
|
||||||
_, _, e1 := unix.Syscall(
|
_, _, e1 := unix.Syscall(
|
||||||
|
@ -101,6 +103,7 @@ func (p *CommandProcessor) handleSignAction(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("identified id system: %s", idSystem)
|
log.Debugf("identified id system: %s", idSystem)
|
||||||
|
|
||||||
switch command.System {
|
switch command.System {
|
||||||
|
@ -109,19 +112,21 @@ func (p *CommandProcessor) handleSignAction(
|
||||||
san := command.Content2
|
san := command.Content2
|
||||||
subject := command.Content3
|
subject := command.Content3
|
||||||
|
|
||||||
if content, err := p.signX509Certificate(idSystem, command.Days, command.Spkac, request, san, subject); err != nil {
|
content, err := p.signX509Certificate(idSystem, command.Days, command.Spkac, request, san, subject)
|
||||||
return nil, err
|
if err != nil {
|
||||||
} else {
|
return nil, fmt.Errorf("could not sign X.509 certificate: %w", err)
|
||||||
return datastructures.NewSignResponse(command.Version, content), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return datastructures.NewSignResponse(command.Version, content), nil
|
||||||
case CsOpenPGP:
|
case CsOpenPGP:
|
||||||
pubKey := command.Content1
|
pubKey := command.Content1
|
||||||
|
|
||||||
if content, err := p.signOpenpgpKey(idSystem, command.Days, pubKey); err != nil {
|
content, err := p.signOpenpgpKey(idSystem, command.Days, pubKey)
|
||||||
return nil, err
|
if err != nil {
|
||||||
} else {
|
return nil, fmt.Errorf("could not sign OpenPGP key: %w", err)
|
||||||
return datastructures.NewSignResponse(command.Version, content), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return datastructures.NewSignResponse(command.Version, content), nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("sign not implemented for crypto system %s", idSystem.System)
|
return nil, fmt.Errorf("sign not implemented for crypto system %s", idSystem.System)
|
||||||
}
|
}
|
||||||
|
@ -144,71 +149,78 @@ func (p *CommandProcessor) handleRevokeAction(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("identified id system: %+v", idSystem)
|
log.Debugf("identified id system: %+v", idSystem)
|
||||||
|
|
||||||
switch command.System {
|
switch command.System {
|
||||||
case CsX509:
|
case CsX509:
|
||||||
request := command.Content1
|
request := command.Content1
|
||||||
clientHash := command.Content3
|
clientHash := command.Content3
|
||||||
if content, err := p.revokeX509(idSystem, request, clientHash); err != nil {
|
|
||||||
return nil, err
|
content, err := p.revokeX509(idSystem, request, clientHash)
|
||||||
} else {
|
if err != nil {
|
||||||
return datastructures.NewRevokeResponse(command.Version, content), nil
|
return nil, fmt.Errorf("could not revoke X.509 certificate: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return datastructures.NewRevokeResponse(command.Version, content), nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("revoke not implemented for crypto system %s", idSystem.System)
|
return nil, fmt.Errorf("revoke not implemented for crypto system %s", idSystem.System)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type IdSystemParameters struct {
|
type IDSystemParams struct {
|
||||||
System *CryptoSystem
|
System *CryptoSystem
|
||||||
Root interface{}
|
Root interface{}
|
||||||
Profile interface{}
|
Profile interface{}
|
||||||
MessageDigestAlgorithm interface{}
|
MessageDigestAlgorithm interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *IdSystemParameters) String() string {
|
func (s *IDSystemParams) String() string {
|
||||||
return fmt.Sprintf("%s r:%s p:%s m:%s", s.System, s.Root, s.Profile, s.MessageDigestAlgorithm)
|
return fmt.Sprintf("%s r:%s p:%s m:%s", s.System, s.Root, s.Profile, s.MessageDigestAlgorithm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *CommandProcessor) checkIdentitySystem(
|
func (p *CommandProcessor) checkIdentitySystem(
|
||||||
systemId shared.CryptoSystemId,
|
systemID shared.CryptoSystemID,
|
||||||
rootId shared.CryptoSystemRootId,
|
rootID shared.CryptoSystemRootID,
|
||||||
profileId shared.CertificateProfileId,
|
profileID shared.CertificateProfileID,
|
||||||
algorithmId shared.MessageDigestAlgorithmId,
|
algorithmID shared.SignatureAlgorithmID,
|
||||||
) (*IdSystemParameters, error) {
|
) (*IDSystemParams, error) {
|
||||||
cryptoSystem, ok := p.CryptoSystems[systemId]
|
cryptoSystem, ok := p.CryptoSystems[systemID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"unsupported crypto system %d",
|
"unsupported crypto system %d",
|
||||||
systemId,
|
systemID,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
root, ok := p.CryptoSystems[systemId].Roots[rootId]
|
|
||||||
|
root, ok := p.CryptoSystems[systemID].Roots[rootID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"unsupported root %d for crypto system %s",
|
"unsupported root %d for crypto system %s",
|
||||||
rootId,
|
rootID,
|
||||||
cryptoSystem,
|
cryptoSystem,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
profile, ok := p.CryptoSystems[systemId].Profiles[profileId]
|
|
||||||
|
profile, ok := p.CryptoSystems[systemID].Profiles[profileID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"invalid profile %d for crypto system %s",
|
"invalid profile %d for crypto system %s",
|
||||||
profileId,
|
profileID,
|
||||||
cryptoSystem,
|
cryptoSystem,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
mdAlgorithm, ok := p.CryptoSystems[systemId].DigestAlgorithms[algorithmId]
|
|
||||||
|
mdAlgorithm, ok := p.CryptoSystems[systemID].DigestAlgorithms[algorithmID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"unsupported digest algorithm %d for crypto system %s",
|
"unsupported digest algorithm %d for crypto system %s",
|
||||||
algorithmId,
|
algorithmID,
|
||||||
cryptoSystem,
|
cryptoSystem,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return &IdSystemParameters{
|
|
||||||
|
return &IDSystemParams{
|
||||||
System: cryptoSystem,
|
System: cryptoSystem,
|
||||||
Root: root,
|
Root: root,
|
||||||
Profile: profile,
|
Profile: profile,
|
||||||
|
@ -216,8 +228,8 @@ func (p *CommandProcessor) checkIdentitySystem(
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *CommandProcessor) revokeX509(system *IdSystemParameters, request []byte, clientHash []byte) ([]byte, error) {
|
func (p *CommandProcessor) revokeX509(system *IDSystemParams, request []byte, clientHash []byte) ([]byte, error) {
|
||||||
x509Root := system.Root.(*x509_ops.Root)
|
x509Root := system.Root.(*x509ops.Root)
|
||||||
signatureAlgorithm := system.MessageDigestAlgorithm.(x509.SignatureAlgorithm)
|
signatureAlgorithm := system.MessageDigestAlgorithm.(x509.SignatureAlgorithm)
|
||||||
|
|
||||||
log.Debugf("revoke X.509 for root %s", x509Root)
|
log.Debugf("revoke X.509 for root %s", x509Root)
|
||||||
|
@ -232,17 +244,19 @@ func (p *CommandProcessor) revokeX509(system *IdSystemParameters, request []byte
|
||||||
if len(request) > 0 {
|
if len(request) > 0 {
|
||||||
_, err = x509Root.RevokeCertificate(request)
|
_, err = x509Root.RevokeCertificate(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not revoke certificate / create CRL: %v", err)
|
return nil, fmt.Errorf("could not revoke certificate / create CRL: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
crlBytes, newHash, err = x509Root.GenerateCrl(signatureAlgorithm)
|
crlBytes, newHash, err = x509Root.GenerateCrl(signatureAlgorithm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not generate a new CRL for root %s: %v", x509Root, err)
|
return nil, fmt.Errorf("could not generate a new CRL for root %s: %w", x509Root, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("crlBytes: %d", len(crlBytes))
|
log.Debugf("crlBytes: %d", len(crlBytes))
|
||||||
|
|
||||||
var content []byte
|
var content []byte
|
||||||
|
|
||||||
oldCrlFile := x509Root.GetCrlFileName(string(clientHash))
|
oldCrlFile := x509Root.GetCrlFileName(string(clientHash))
|
||||||
newCrlFile := x509Root.GetCrlFileName(hex.EncodeToString(newHash[:]))
|
newCrlFile := x509Root.GetCrlFileName(hex.EncodeToString(newHash[:]))
|
||||||
|
|
||||||
|
@ -254,8 +268,10 @@ func (p *CommandProcessor) revokeX509(system *IdSystemParameters, request []byte
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("could not generate xdelta: %v", err)
|
log.Warnf("could not generate xdelta: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Tracef("xdelta produced %d bytes", len(content))
|
log.Tracef("xdelta produced %d bytes", len(content))
|
||||||
}
|
}
|
||||||
|
|
||||||
if content == nil {
|
if content == nil {
|
||||||
content = pem.EncodeToMemory(&pem.Block{
|
content = pem.EncodeToMemory(&pem.Block{
|
||||||
Type: "X509 CRL",
|
Type: "X509 CRL",
|
||||||
|
@ -270,50 +286,56 @@ func (p *CommandProcessor) revokeX509(system *IdSystemParameters, request []byte
|
||||||
func (p *CommandProcessor) buildXDelta(oldFile string, newFile string) ([]byte, error) {
|
func (p *CommandProcessor) buildXDelta(oldFile string, newFile string) ([]byte, error) {
|
||||||
patchFile, err := ioutil.TempFile(os.TempDir(), "*.patch")
|
patchFile, err := ioutil.TempFile(os.TempDir(), "*.patch")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not create temporary file for patch: %v", err)
|
return nil, fmt.Errorf("could not create temporary file for patch: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
patchName := patchFile.Name()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := os.Remove(patchFile.Name()); err != nil {
|
if err := os.Remove(patchName); err != nil {
|
||||||
log.Warnf("could not remove temporary file %s: %v", patchFile.Name(), err)
|
log.Warnf("could not remove temporary file %s: %v", patchName, err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err = patchFile.Close(); err != nil {
|
if err = patchFile.Close(); err != nil {
|
||||||
return nil, fmt.Errorf("could not close temporary file: %v", err)
|
return nil, fmt.Errorf("could not close temporary file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
cmd := exec.Command(p.Settings.XDeltaPath, "delta", oldFile, newFile, patchFile.Name())
|
|
||||||
|
// #nosec G204 no parameters are based on user input
|
||||||
|
cmd := exec.Command(p.Settings.XDeltaPath, "delta", oldFile, newFile, patchName)
|
||||||
cmd.Stdout = buf
|
cmd.Stdout = buf
|
||||||
cmd.Stderr = buf
|
cmd.Stderr = buf
|
||||||
|
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err.(type) {
|
var e *exec.ExitError
|
||||||
case *exec.ExitError:
|
if !errors.As(err, &e) || e.ExitCode() != 1 {
|
||||||
if err.(*exec.ExitError).ExitCode() == 1 {
|
|
||||||
// xdelta delta exits with status code 1 if a delta has been found
|
// xdelta delta exits with status code 1 if a delta has been found
|
||||||
break
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"xdelta command '%s' did not work correctly: %v\noutput was:\n%s",
|
"xdelta command '%s' did not work correctly: %w\noutput was:\n%s",
|
||||||
strings.Join(cmd.Args, " "),
|
|
||||||
err,
|
|
||||||
buf.String(),
|
|
||||||
)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf(
|
|
||||||
"xdelta command '%s' did not work correctly: %v\noutput was:\n%s",
|
|
||||||
strings.Join(cmd.Args, " "),
|
strings.Join(cmd.Args, " "),
|
||||||
err,
|
err,
|
||||||
buf.String(),
|
buf.String(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ioutil.ReadFile(patchFile.Name())
|
|
||||||
|
return ioutil.ReadFile(patchName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *CommandProcessor) signX509Certificate(system *IdSystemParameters, days uint16, spkac uint8, request []byte, san []byte, subject []byte) ([]byte, error) {
|
func (p *CommandProcessor) signX509Certificate(
|
||||||
x509Root := system.Root.(*x509_ops.Root)
|
system *IDSystemParams,
|
||||||
|
days uint16,
|
||||||
|
spkac uint8,
|
||||||
|
request []byte,
|
||||||
|
san []byte,
|
||||||
|
subject []byte,
|
||||||
|
) ([]byte, error) {
|
||||||
|
x509Root := system.Root.(*x509ops.Root)
|
||||||
signatureAlgorithm := system.MessageDigestAlgorithm.(x509.SignatureAlgorithm)
|
signatureAlgorithm := system.MessageDigestAlgorithm.(x509.SignatureAlgorithm)
|
||||||
profile := system.Profile.(*x509_ops.Profile)
|
profile := system.Profile.(*x509ops.Profile)
|
||||||
|
|
||||||
log.Debugf(
|
log.Debugf(
|
||||||
"sign X.509 certificate for root %s using profile %s and signature algorithm %s",
|
"sign X.509 certificate for root %s using profile %s and signature algorithm %s",
|
||||||
|
@ -330,7 +352,7 @@ func (p *CommandProcessor) signX509Certificate(system *IdSystemParameters, days
|
||||||
content, err := x509Root.SignCertificate(
|
content, err := x509Root.SignCertificate(
|
||||||
profile,
|
profile,
|
||||||
signatureAlgorithm,
|
signatureAlgorithm,
|
||||||
&x509_ops.SigningRequestParameters{
|
&x509ops.SigningRequestParameters{
|
||||||
Request: request,
|
Request: request,
|
||||||
Subject: subject,
|
Subject: subject,
|
||||||
SubjectAlternativeNames: san,
|
SubjectAlternativeNames: san,
|
||||||
|
@ -339,21 +361,21 @@ func (p *CommandProcessor) signX509Certificate(system *IdSystemParameters, days
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not sign X.509 CSR with root %s and profile %s: %v", x509Root, profile, err)
|
return nil, fmt.Errorf("could not sign X.509 CSR with root %s and profile %s: %w", x509Root, profile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return content, nil
|
return content, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *CommandProcessor) signOpenpgpKey(system *IdSystemParameters, days uint16, pubKey []byte) ([]byte, error) {
|
func (p *CommandProcessor) signOpenpgpKey(system *IDSystemParams, days uint16, pubKey []byte) ([]byte, error) {
|
||||||
openPgpRoot := system.Root.(*openpgp_ops.OpenPGPRoot)
|
openPgpRoot := system.Root.(*openpgpops.OpenPGPRoot)
|
||||||
signatureAlgorithm := system.MessageDigestAlgorithm.(crypto.Hash)
|
signatureAlgorithm := system.MessageDigestAlgorithm.(crypto.Hash)
|
||||||
|
|
||||||
log.Debugf("sign openpgp for root %s", openPgpRoot)
|
log.Debugf("sign openpgpops for root %s", openPgpRoot)
|
||||||
|
|
||||||
content, err := openPgpRoot.SignPublicKey(pubKey, signatureAlgorithm, days)
|
content, err := openPgpRoot.SignPublicKey(pubKey, signatureAlgorithm, days)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not sign openpgp public key with root %s: %v", openPgpRoot, err)
|
return nil, fmt.Errorf("could not sign openpgpops public key with root %s: %w", openPgpRoot, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return content, nil
|
return content, nil
|
||||||
|
@ -364,18 +386,21 @@ func NewCommandProcessorSettings() *CommandProcessorSettings {
|
||||||
if !ok {
|
if !ok {
|
||||||
caBasedir = "."
|
caBasedir = "."
|
||||||
}
|
}
|
||||||
|
|
||||||
gpgKeyringDir, ok := os.LookupEnv("SIGNER_GPG_KEYRING_DIR")
|
gpgKeyringDir, ok := os.LookupEnv("SIGNER_GPG_KEYRING_DIR")
|
||||||
if !ok {
|
if !ok {
|
||||||
gpgKeyringDir = "."
|
gpgKeyringDir = "."
|
||||||
}
|
}
|
||||||
gpgUidEmail, ok := os.LookupEnv("SIGNER_GPG_ID")
|
|
||||||
|
gpgUIDEmail, ok := os.LookupEnv("SIGNER_GPG_ID")
|
||||||
if !ok {
|
if !ok {
|
||||||
gpgUidEmail = "gpg@cacert.org"
|
gpgUIDEmail = "gpg@cacert.org"
|
||||||
}
|
}
|
||||||
|
|
||||||
return &CommandProcessorSettings{
|
return &CommandProcessorSettings{
|
||||||
CABaseDir: caBasedir,
|
CABaseDir: caBasedir,
|
||||||
OpenPGPKeyRingDir: gpgKeyringDir,
|
OpenPGPKeyRingDir: gpgKeyringDir,
|
||||||
OpenPGPUidEmail: gpgUidEmail,
|
OpenPGPUidEmail: gpgUIDEmail,
|
||||||
XDeltaPath: "/usr/bin/xdelta",
|
XDeltaPath: "/usr/bin/xdelta",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func StringAsBigInt(data []byte) (*big.Int, error) {
|
|
||||||
dataString := strings.TrimSpace(string(data))
|
|
||||||
parseInt, err := strconv.ParseInt(dataString, 16, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not parse %s as big int: %v", dataString, err)
|
|
||||||
}
|
|
||||||
return big.NewInt(parseInt), nil
|
|
||||||
}
|
|
|
@ -6,9 +6,9 @@ import (
|
||||||
|
|
||||||
type CryptoSystem struct {
|
type CryptoSystem struct {
|
||||||
Name string
|
Name string
|
||||||
Roots map[shared.CryptoSystemRootId]interface{}
|
Roots map[shared.CryptoSystemRootID]interface{}
|
||||||
Profiles map[shared.CertificateProfileId]interface{}
|
Profiles map[shared.CertificateProfileID]interface{}
|
||||||
DigestAlgorithms map[shared.MessageDigestAlgorithmId]interface{}
|
DigestAlgorithms map[shared.SignatureAlgorithmID]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (system CryptoSystem) String() string {
|
func (system CryptoSystem) String() string {
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package openpgp_ops
|
package openpgpops
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto"
|
"crypto"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
@ -13,6 +14,8 @@ import (
|
||||||
"golang.org/x/crypto/openpgp/packet"
|
"golang.org/x/crypto/openpgp/packet"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const hoursInADay = 24
|
||||||
|
|
||||||
type OpenPGPRoot struct {
|
type OpenPGPRoot struct {
|
||||||
Name string
|
Name string
|
||||||
SecretKeyRing string
|
SecretKeyRing string
|
||||||
|
@ -22,24 +25,27 @@ type OpenPGPRoot struct {
|
||||||
func (r *OpenPGPRoot) SignPublicKey(pubKey []byte, algorithm crypto.Hash, days uint16) ([]byte, error) {
|
func (r *OpenPGPRoot) SignPublicKey(pubKey []byte, algorithm crypto.Hash, days uint16) ([]byte, error) {
|
||||||
signingKey, err := r.findSigningKey(r.Identifier)
|
signingKey, err := r.findSigningKey(r.Identifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not find a signing key matching %s: %v", r.Identifier, err)
|
return nil, fmt.Errorf("could not find a signing key matching %s: %w", r.Identifier, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pubKeyRing, err := openpgp.ReadKeyRing(bytes.NewReader(pubKey))
|
pubKeyRing, err := openpgp.ReadKeyRing(bytes.NewReader(pubKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not read openpgp keyring: %v", err)
|
return nil, fmt.Errorf("could not read openpgpops keyring: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
output := bytes.NewBuffer([]byte{})
|
output := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
armorOutput, err := armor.Encode(output, "PGP PUBLIC KEY BLOCK", map[string]string{})
|
armorOutput, err := armor.Encode(output, "PGP PUBLIC KEY BLOCK", map[string]string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not create ASCII armor wrapper for openpgp output: %v", err)
|
return nil, fmt.Errorf("could not create ASCII armor wrapper for openpgpops output: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pe := range pubKeyRing {
|
for _, pe := range pubKeyRing {
|
||||||
log.Tracef("found %+v", pe.PrimaryKey.KeyIdString())
|
log.Tracef("found %+v", pe.PrimaryKey.KeyIdString())
|
||||||
|
|
||||||
for _, i := range pe.Identities {
|
for _, i := range pe.Identities {
|
||||||
expiry := calculateExpiry(i, days)
|
expiry := calculateExpiry(i, days)
|
||||||
|
|
||||||
if !i.SelfSignature.KeyExpired(time.Now()) {
|
if !i.SelfSignature.KeyExpired(time.Now()) {
|
||||||
sig := &packet.Signature{
|
sig := &packet.Signature{
|
||||||
SigType: packet.SigTypeGenericCert,
|
SigType: packet.SigTypeGenericCert,
|
||||||
|
@ -52,14 +58,16 @@ func (r *OpenPGPRoot) SignPublicKey(pubKey []byte, algorithm crypto.Hash, days u
|
||||||
if err := sig.SignUserId(i.Name, pe.PrimaryKey, signingKey.PrivateKey, &packet.Config{
|
if err := sig.SignUserId(i.Name, pe.PrimaryKey, signingKey.PrivateKey, &packet.Config{
|
||||||
DefaultHash: algorithm,
|
DefaultHash: algorithm,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, fmt.Errorf("could not sign identity %s: %v", i.Name, err)
|
return nil, fmt.Errorf("could not sign identity %s: %w", i.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
i.Signatures = append(i.Signatures, sig)
|
i.Signatures = append(i.Signatures, sig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = pe.Serialize(armorOutput); err != nil {
|
if err = pe.Serialize(armorOutput); err != nil {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"could not write signed public key %s to output: %v",
|
"could not write signed public key %s to output: %w",
|
||||||
pe.PrimaryKey.KeyIdString(),
|
pe.PrimaryKey.KeyIdString(),
|
||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
|
@ -67,7 +75,7 @@ func (r *OpenPGPRoot) SignPublicKey(pubKey []byte, algorithm crypto.Hash, days u
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = armorOutput.Close(); err != nil {
|
if err = armorOutput.Close(); err != nil {
|
||||||
return nil, fmt.Errorf("could not close output stream: %v", err)
|
return nil, fmt.Errorf("could not close output stream: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Tracef("signed public key\n%s", output.String())
|
log.Tracef("signed public key\n%s", output.String())
|
||||||
|
@ -77,35 +85,41 @@ func (r *OpenPGPRoot) SignPublicKey(pubKey []byte, algorithm crypto.Hash, days u
|
||||||
|
|
||||||
func calculateExpiry(i *openpgp.Identity, days uint16) *uint32 {
|
func calculateExpiry(i *openpgp.Identity, days uint16) *uint32 {
|
||||||
maxExpiry := time.Second * time.Duration(*i.SelfSignature.KeyLifetimeSecs)
|
maxExpiry := time.Second * time.Duration(*i.SelfSignature.KeyLifetimeSecs)
|
||||||
calcExpiry := time.Hour * 24 * time.Duration(days)
|
calcExpiry := time.Hour * hoursInADay * time.Duration(days)
|
||||||
|
|
||||||
if calcExpiry > maxExpiry {
|
if calcExpiry > maxExpiry {
|
||||||
calcExpiry = maxExpiry
|
calcExpiry = maxExpiry
|
||||||
}
|
}
|
||||||
|
|
||||||
expirySeconds := uint32(calcExpiry.Seconds())
|
expirySeconds := uint32(calcExpiry.Seconds())
|
||||||
|
|
||||||
return &expirySeconds
|
return &expirySeconds
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *OpenPGPRoot) findSigningKey(identifier string) (*openpgp.Entity, error) {
|
func (r *OpenPGPRoot) findSigningKey(identifier string) (*openpgp.Entity, error) {
|
||||||
keyring, err := os.Open(r.SecretKeyRing)
|
keyring, err := os.Open(r.SecretKeyRing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not open secret keyring: %v", err)
|
return nil, fmt.Errorf("could not open secret keyring: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() { _ = keyring.Close() }()
|
defer func() { _ = keyring.Close() }()
|
||||||
|
|
||||||
el, err := openpgp.ReadKeyRing(keyring)
|
el, err := openpgp.ReadKeyRing(keyring)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not read keyring: %v", err)
|
return nil, fmt.Errorf("could not read keyring: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, e := range el {
|
for _, e := range el {
|
||||||
log.Tracef("found %s", e.PrimaryKey.KeyIdString())
|
log.Tracef("found %s", e.PrimaryKey.KeyIdString())
|
||||||
|
|
||||||
for _, i := range e.Identities {
|
for _, i := range e.Identities {
|
||||||
if i.UserId.Email == identifier && len(e.Revocations) == 0 && !i.SelfSignature.KeyExpired(time.Now()) {
|
if i.UserId.Email == identifier && len(e.Revocations) == 0 && !i.SelfSignature.KeyExpired(time.Now()) {
|
||||||
return e, nil
|
return e, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("no matching key found")
|
|
||||||
|
return nil, errors.New("no matching key found")
|
||||||
}
|
}
|
||||||
|
|
||||||
type OpenPGPProfile struct {
|
type OpenPGPProfile struct {
|
|
@ -33,6 +33,7 @@ type PortHandler struct {
|
||||||
|
|
||||||
func (p *PortHandler) MainLoop() {
|
func (p *PortHandler) MainLoop() {
|
||||||
count := 0
|
count := 0
|
||||||
|
|
||||||
for {
|
for {
|
||||||
go p.Receive()
|
go p.Receive()
|
||||||
|
|
||||||
|
@ -40,9 +41,12 @@ func (p *PortHandler) MainLoop() {
|
||||||
case command := <-p.commandChan:
|
case command := <-p.commandChan:
|
||||||
if command == nil {
|
if command == nil {
|
||||||
log.Infof("command channel has been closed. stopping execution.")
|
log.Infof("command channel has been closed. stopping execution.")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("received command %v", command)
|
log.Debugf("received command %v", command)
|
||||||
|
|
||||||
response, err := p.processor.Process(command)
|
response, err := p.processor.Process(command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("error processing command: %v", err)
|
log.Errorf("error processing command: %v", err)
|
||||||
|
@ -61,68 +65,62 @@ func (p *PortHandler) MainLoop() {
|
||||||
|
|
||||||
// Receive a request and generate a request data structure
|
// Receive a request and generate a request data structure
|
||||||
func (p *PortHandler) Receive() {
|
func (p *PortHandler) Receive() {
|
||||||
header, err := shared.ReceiveBytes(p.port, 1, waitForInitialByte)
|
if err := p.receiveHandshake(); err != nil {
|
||||||
if err != nil {
|
|
||||||
p.errors <- err
|
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
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var command *datastructures.SignerRequest
|
var command *datastructures.SignerRequest
|
||||||
for {
|
|
||||||
if tries <= 0 {
|
for tries := maxTriesPerBlock; tries > 0; tries-- {
|
||||||
p.errors <- errors.New("tried reading block too often")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
tries--
|
|
||||||
if err := shared.SendBytes(p.port, []byte{shared.AckByte}); err != nil {
|
if err := shared.SendBytes(p.port, []byte{shared.AckByte}); err != nil {
|
||||||
p.errors <- fmt.Errorf("could not write ACK byte: %v", err)
|
p.errors <- fmt.Errorf("could not write ACK byte: %w", err)
|
||||||
break
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
lengthBytes, err := shared.ReceiveBytes(p.port, shared.LengthFieldSize, waitForLengthBytes)
|
lengthBytes, err := shared.ReceiveBytes(p.port, shared.LengthFieldSize, waitForLengthBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.errors <- fmt.Errorf("could not read lenght bytes: %v", err)
|
p.errors <- fmt.Errorf("could not read length bytes: %w", err)
|
||||||
break
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
blockLength := datastructures.Decode24BitLength(lengthBytes)
|
blockLength := datastructures.Decode24BitLength(lengthBytes)
|
||||||
|
|
||||||
blockData, err := shared.ReceiveBytes(p.port, blockLength, waitForBlock)
|
blockData, err := shared.ReceiveBytes(p.port, blockLength, waitForBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.errors <- fmt.Errorf("could not read data block: %v", err)
|
p.errors <- fmt.Errorf("could not read data block: %w", err)
|
||||||
if !p.requestResend() {
|
if !p.requestResend() {
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
checkSum, err := shared.ReceiveBytes(p.port, shared.CheckSumFieldSize, waitForChecksumByte)
|
checkSum, err := shared.ReceiveBytes(p.port, shared.CheckSumFieldSize, waitForChecksumByte)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.errors <- fmt.Errorf("could not read checksum byte: %v", err)
|
p.errors <- fmt.Errorf("could not read checksum byte: %w", err)
|
||||||
break
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
calculated := datastructures.CalculateXorCheckSum([][]byte{lengthBytes, blockData})
|
calculated := datastructures.CalculateXorCheckSum([][]byte{lengthBytes, blockData})
|
||||||
if checkSum[0] != calculated {
|
if checkSum[0] != calculated {
|
||||||
p.errors <- fmt.Errorf("CRC error. expected 0x%02x, got 0x%02x", calculated, checkSum[0])
|
p.errors <- fmt.Errorf("CRC error. expected 0x%02x, got 0x%02x", calculated, checkSum[0])
|
||||||
if !p.requestResend() {
|
if !p.requestResend() {
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
trailer, err := shared.ReceiveBytes(p.port, shared.TrailerFieldSize, waitForTrailerBytes)
|
trailer, err := shared.ReceiveBytes(p.port, shared.TrailerFieldSize, waitForTrailerBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.errors <- fmt.Errorf("could not read trailer bytes: %v", err)
|
p.errors <- fmt.Errorf("could not read trailer bytes: %w", err)
|
||||||
break
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if string(trailer) != shared.MagicTrailer {
|
if string(trailer) != shared.MagicTrailer {
|
||||||
|
@ -131,70 +129,99 @@ func (p *PortHandler) Receive() {
|
||||||
shared.MagicTrailer,
|
shared.MagicTrailer,
|
||||||
trailer,
|
trailer,
|
||||||
)
|
)
|
||||||
|
|
||||||
if !p.requestResend() {
|
if !p.requestResend() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
command, err = datastructures.SignerRequestFromData(blockData)
|
command, err = datastructures.SignerRequestFromData(blockData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.errors <- fmt.Errorf("failed to parse block as signer request: %v", err)
|
p.errors <- fmt.Errorf("failed to parse block as signer request: %w", err)
|
||||||
break
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := shared.SendBytes(p.port, []byte{shared.AckByte}); err != nil {
|
if err := shared.SendBytes(p.port, []byte{shared.AckByte}); err != nil {
|
||||||
p.errors <- fmt.Errorf("failed to send ACK byte: %v", err)
|
p.errors <- fmt.Errorf("failed to send ACK byte: %w", err)
|
||||||
break
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
p.commandChan <- command
|
p.commandChan <- command
|
||||||
break
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.errors <- errors.New("tried reading block too often")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PortHandler) receiveHandshake() error {
|
||||||
|
header, err := shared.ReceiveBytes(p.port, 1, waitForInitialByte)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not receive initial byte: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if header[0] != shared.HandshakeByte {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"unexpected byte 0x%x expected 0x%x",
|
||||||
|
header[0],
|
||||||
|
shared.HandshakeByte,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PortHandler) requestResend() bool {
|
func (p *PortHandler) requestResend() bool {
|
||||||
if err := shared.SendBytes(p.port, []byte{shared.ResendByte}); err != nil {
|
if err := shared.SendBytes(p.port, []byte{shared.ResendByte}); err != nil {
|
||||||
p.errors <- err
|
p.errors <- err
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a response to the client
|
// Send a response to the client
|
||||||
func (p *PortHandler) SendResponse(response *datastructures.SignerResponse) error {
|
func (p *PortHandler) SendResponse(response *datastructures.SignerResponse) error {
|
||||||
if err := shared.SendBytes(p.port, []byte{shared.HandshakeByte}); err != nil {
|
if err := shared.SendBytes(p.port, []byte{shared.HandshakeByte}); err != nil {
|
||||||
return err
|
return fmt.Errorf("could not send handshake: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ack, err := shared.ReceiveBytes(p.port, 1, waitForHandShake); err != nil {
|
if ack, err := shared.ReceiveBytes(p.port, 1, waitForHandShake); err != nil {
|
||||||
return err
|
return fmt.Errorf("could not receive ACK: %w", err)
|
||||||
} else if ack[0] != shared.AckByte {
|
} else if ack[0] != shared.AckByte {
|
||||||
return errors.New(fmt.Sprintf("invalid ack byte 0x%02x", ack[0]))
|
return fmt.Errorf("invalid ack byte 0x%02x", ack[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
tryAgain := true
|
tryAgain := true
|
||||||
for tryAgain {
|
for tryAgain {
|
||||||
data := response.Serialize()
|
data := response.Serialize()
|
||||||
if err := shared.SendBytes(p.port, data); err != nil {
|
if err := shared.SendBytes(p.port, data); err != nil {
|
||||||
return err
|
return fmt.Errorf("could not send data block: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
checksum := datastructures.CalculateXorCheckSum([][]byte{data})
|
checksum := datastructures.CalculateXorCheckSum([][]byte{data})
|
||||||
if err := shared.SendBytes(p.port, []byte{checksum}); err != nil {
|
if err := shared.SendBytes(p.port, []byte{checksum}); err != nil {
|
||||||
return err
|
return fmt.Errorf("could not send checksum: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := shared.SendBytes(p.port, []byte(shared.MagicTrailer)); err != nil {
|
if err := shared.SendBytes(p.port, []byte(shared.MagicTrailer)); err != nil {
|
||||||
return err
|
return fmt.Errorf("could not send trailer: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ack, err := shared.ReceiveBytes(p.port, 1, waitForHandShake); err != nil {
|
ack, err := shared.ReceiveBytes(p.port, 1, waitForHandShake)
|
||||||
return err
|
if err != nil {
|
||||||
} else if ack[0] == shared.AckByte {
|
return fmt.Errorf("could not receive ACK: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ack[0] == shared.AckByte {
|
||||||
tryAgain = false
|
tryAgain = false
|
||||||
} else if ack[0] != 0x11 {
|
} else {
|
||||||
return errors.New(fmt.Sprintf("invalid ack byte 0x%02x", ack[0]))
|
return fmt.Errorf("invalid ack byte 0x%02x", ack[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,6 +230,7 @@ func (p *PortHandler) SendResponse(response *datastructures.SignerResponse) erro
|
||||||
|
|
||||||
func NewSignerProcess(port io.ReadWriteCloser) *PortHandler {
|
func NewSignerProcess(port io.ReadWriteCloser) *PortHandler {
|
||||||
errorChan := make(chan error)
|
errorChan := make(chan error)
|
||||||
|
|
||||||
return &PortHandler{
|
return &PortHandler{
|
||||||
port: port,
|
port: port,
|
||||||
errors: errorChan,
|
errors: errorChan,
|
||||||
|
|
|
@ -7,65 +7,65 @@ import (
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"git.cacert.org/cacert-gosigner/shared"
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
"git.cacert.org/cacert-gosigner/signer/openpgp_ops"
|
"git.cacert.org/cacert-gosigner/signer/openpgpops"
|
||||||
"git.cacert.org/cacert-gosigner/signer/x509_ops"
|
"git.cacert.org/cacert-gosigner/signer/x509ops"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CsX509 shared.CryptoSystemId = 1
|
CsX509 shared.CryptoSystemID = 1
|
||||||
CsOpenPGP shared.CryptoSystemId = 2
|
CsOpenPGP shared.CryptoSystemID = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
X509RootDefault shared.CryptoSystemRootId = 0
|
X509RootDefault shared.CryptoSystemRootID = 0
|
||||||
X509RootClass3 shared.CryptoSystemRootId = 1
|
X509RootClass3 shared.CryptoSystemRootID = 1
|
||||||
// The following roots existed in the old server.pl but had
|
// The following roots existed in the old server.pl but had
|
||||||
// no profile configurations and were thus unusable
|
// no profile configurations and were thus unusable
|
||||||
//
|
//
|
||||||
// X509RootClass3s shared.CryptoSystemRootId = 2
|
// X509RootClass3s shared.CryptoSystemRootID = 2
|
||||||
// X509Root3 shared.CryptoSystemRootId = 3
|
// X509Root3 shared.CryptoSystemRootID = 3
|
||||||
// X509Root4 shared.CryptoSystemRootId = 4
|
// X509Root4 shared.CryptoSystemRootID = 4
|
||||||
// X509Root5 shared.CryptoSystemRootId = 5
|
// X509Root5 shared.CryptoSystemRootID = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
X509ProfileClient shared.CertificateProfileId = 0
|
X509ProfileClient shared.CertificateProfileID = 0
|
||||||
X509ProfileClientOrg shared.CertificateProfileId = 1
|
X509ProfileClientOrg shared.CertificateProfileID = 1
|
||||||
X509ProfileClientCodesign shared.CertificateProfileId = 2
|
X509ProfileClientCodesign shared.CertificateProfileID = 2
|
||||||
X509ProfileServer shared.CertificateProfileId = 5
|
X509ProfileServer shared.CertificateProfileID = 5
|
||||||
X509ProfileServerOrg shared.CertificateProfileId = 6
|
X509ProfileServerOrg shared.CertificateProfileID = 6
|
||||||
X509ProfileOCSP shared.CertificateProfileId = 8
|
X509ProfileOCSP shared.CertificateProfileID = 8
|
||||||
X509ProfileTimestamp shared.CertificateProfileId = 9
|
X509ProfileTimestamp shared.CertificateProfileID = 9
|
||||||
|
|
||||||
// the following profiles where valid options in the original signer code but had no configurations
|
// the following profiles where valid options in the original signer code but had no configurations
|
||||||
//
|
//
|
||||||
// X509ProfileClientMachine shared.CertificateProfileId = 3 // no configuration on original signer
|
// X509ProfileClientMachine shared.CertificateProfileID = 3 // no configuration on original signer
|
||||||
// X509ProfileClientAds shared.CertificateProfileId = 4 // no configuration on original signer
|
// X509ProfileClientAds shared.CertificateProfileID = 4 // no configuration on original signer
|
||||||
// X509ProfileServerJabber shared.CertificateProfileId = 7 // no configuration on original signer
|
// X509ProfileServerJabber shared.CertificateProfileID = 7 // no configuration on original signer
|
||||||
// X509ProfileProxy shared.CertificateProfileId = 10 // no configuration on original signer
|
// X509ProfileProxy shared.CertificateProfileID = 10 // no configuration on original signer
|
||||||
// X509ProfileSubCA shared.CertificateProfileId = 11 // no configuration on original signer
|
// X509ProfileSubCA shared.CertificateProfileID = 11 // no configuration on original signer
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
X509MDDefault shared.MessageDigestAlgorithmId = 0
|
X509MDDefault shared.SignatureAlgorithmID = 0
|
||||||
X509MDMd5 shared.MessageDigestAlgorithmId = 1
|
X509MDMd5 shared.SignatureAlgorithmID = 1
|
||||||
X509MDSha1 shared.MessageDigestAlgorithmId = 2
|
X509MDSha1 shared.SignatureAlgorithmID = 2
|
||||||
// X509MDRipeMD160 shared.MessageDigestAlgorithmId = 3 x509 package does not support RIPEMD160
|
// X509MDRipeMD160 shared.SignatureAlgorithmID = 3 x509ops package does not support RIPEMD160
|
||||||
X509MDSha256 shared.MessageDigestAlgorithmId = 8
|
X509MDSha256 shared.SignatureAlgorithmID = 8
|
||||||
X509MDSha384 shared.MessageDigestAlgorithmId = 9
|
X509MDSha384 shared.SignatureAlgorithmID = 9
|
||||||
X509MDSha512 shared.MessageDigestAlgorithmId = 10
|
X509MDSha512 shared.SignatureAlgorithmID = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
OpenPGPRoot0 shared.CryptoSystemRootId = 0
|
OpenPGPRoot0 shared.CryptoSystemRootID = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
OpenPGPDefaultProfile shared.CertificateProfileId = 0
|
OpenPGPDefaultProfile shared.CertificateProfileID = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
OpenPGPDefaultMD shared.MessageDigestAlgorithmId = 0
|
OpenPGPDefaultMD shared.SignatureAlgorithmID = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewCommandProcessor() *CommandProcessor {
|
func NewCommandProcessor() *CommandProcessor {
|
||||||
|
@ -76,9 +76,9 @@ func NewCommandProcessor() *CommandProcessor {
|
||||||
ExtKeyUsage: []x509.ExtKeyUsage{
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||||
x509.ExtKeyUsageEmailProtection,
|
x509.ExtKeyUsageEmailProtection,
|
||||||
x509.ExtKeyUsageClientAuth,
|
x509.ExtKeyUsageClientAuth,
|
||||||
// x509.ExtKeyUsageMicrosoftServerGatedCrypto,
|
// x509ops.ExtKeyUsageMicrosoftServerGatedCrypto,
|
||||||
// 1.3.6.1.4.1.311.10.3.4 msEFS not supported by golang.org/crypto
|
// 1.3.6.1.4.1.311.10.3.4 msEFS not supported by golang.org/crypto
|
||||||
// x509.ExtKeyUsageNetscapeServerGatedCrypto,
|
// x509ops.ExtKeyUsageNetscapeServerGatedCrypto,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
codeSignPrototype := &x509.Certificate{
|
codeSignPrototype := &x509.Certificate{
|
||||||
|
@ -88,10 +88,10 @@ func NewCommandProcessor() *CommandProcessor {
|
||||||
x509.ExtKeyUsageClientAuth,
|
x509.ExtKeyUsageClientAuth,
|
||||||
x509.ExtKeyUsageCodeSigning,
|
x509.ExtKeyUsageCodeSigning,
|
||||||
// 1.3.6.1.4.1.311.2.1.21 msCodeInd not supported by golang.org/crypto
|
// 1.3.6.1.4.1.311.2.1.21 msCodeInd not supported by golang.org/crypto
|
||||||
// x509.ExtKeyUsageMicrosoftCommercialCodeSigning,
|
// x509ops.ExtKeyUsageMicrosoftCommercialCodeSigning,
|
||||||
// x509.ExtKeyUsageMicrosoftServerGatedCrypto,
|
// x509ops.ExtKeyUsageMicrosoftServerGatedCrypto,
|
||||||
// 1.3.6.1.4.1.311.10.3.4 msEFS not supported by golang.org/crypto
|
// 1.3.6.1.4.1.311.10.3.4 msEFS not supported by golang.org/crypto
|
||||||
// x509.ExtKeyUsageNetscapeServerGatedCrypto,
|
// x509ops.ExtKeyUsageNetscapeServerGatedCrypto,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
serverPrototype := &x509.Certificate{
|
serverPrototype := &x509.Certificate{
|
||||||
|
@ -99,8 +99,8 @@ func NewCommandProcessor() *CommandProcessor {
|
||||||
ExtKeyUsage: []x509.ExtKeyUsage{
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||||
x509.ExtKeyUsageClientAuth,
|
x509.ExtKeyUsageClientAuth,
|
||||||
x509.ExtKeyUsageServerAuth,
|
x509.ExtKeyUsageServerAuth,
|
||||||
// x509.ExtKeyUsageMicrosoftServerGatedCrypto,
|
// x509ops.ExtKeyUsageMicrosoftServerGatedCrypto,
|
||||||
// x509.ExtKeyUsageNetscapeServerGatedCrypto,
|
// x509ops.ExtKeyUsageNetscapeServerGatedCrypto,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
ocspPrototype := &x509.Certificate{
|
ocspPrototype := &x509.Certificate{
|
||||||
|
@ -108,8 +108,8 @@ func NewCommandProcessor() *CommandProcessor {
|
||||||
ExtKeyUsage: []x509.ExtKeyUsage{
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||||
x509.ExtKeyUsageServerAuth,
|
x509.ExtKeyUsageServerAuth,
|
||||||
x509.ExtKeyUsageOCSPSigning,
|
x509.ExtKeyUsageOCSPSigning,
|
||||||
// x509.ExtKeyUsageMicrosoftServerGatedCrypto,
|
// x509ops.ExtKeyUsageMicrosoftServerGatedCrypto,
|
||||||
// x509.ExtKeyUsageNetscapeServerGatedCrypto,
|
// x509ops.ExtKeyUsageNetscapeServerGatedCrypto,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
timestampPrototype := &x509.Certificate{
|
timestampPrototype := &x509.Certificate{
|
||||||
|
@ -117,15 +117,15 @@ func NewCommandProcessor() *CommandProcessor {
|
||||||
ExtKeyUsage: []x509.ExtKeyUsage{
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||||
x509.ExtKeyUsageServerAuth,
|
x509.ExtKeyUsageServerAuth,
|
||||||
x509.ExtKeyUsageOCSPSigning,
|
x509.ExtKeyUsageOCSPSigning,
|
||||||
// x509.ExtKeyUsageMicrosoftServerGatedCrypto,
|
// x509ops.ExtKeyUsageMicrosoftServerGatedCrypto,
|
||||||
// x509.ExtKeyUsageNetscapeServerGatedCrypto,
|
// x509ops.ExtKeyUsageNetscapeServerGatedCrypto,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
cryptoSystems := map[shared.CryptoSystemId]*CryptoSystem{
|
cryptoSystems := map[shared.CryptoSystemID]*CryptoSystem{
|
||||||
CsX509: {
|
CsX509: {
|
||||||
Name: "X.509",
|
Name: "X.509",
|
||||||
Roots: map[shared.CryptoSystemRootId]interface{}{
|
Roots: map[shared.CryptoSystemRootID]interface{}{
|
||||||
X509RootDefault: x509_ops.NewRoot(
|
X509RootDefault: x509ops.NewRoot(
|
||||||
settings.CABaseDir,
|
settings.CABaseDir,
|
||||||
"openssl",
|
"openssl",
|
||||||
"CA",
|
"CA",
|
||||||
|
@ -135,7 +135,7 @@ func NewCommandProcessor() *CommandProcessor {
|
||||||
// TODO: parse OCSP endpoints from configuration
|
// TODO: parse OCSP endpoints from configuration
|
||||||
[]string{"http://ocsp.cacert.localhost"},
|
[]string{"http://ocsp.cacert.localhost"},
|
||||||
),
|
),
|
||||||
X509RootClass3: x509_ops.NewRoot(
|
X509RootClass3: x509ops.NewRoot(
|
||||||
settings.CABaseDir,
|
settings.CABaseDir,
|
||||||
"class3",
|
"class3",
|
||||||
"class3",
|
"class3",
|
||||||
|
@ -148,99 +148,99 @@ func NewCommandProcessor() *CommandProcessor {
|
||||||
// The following roots existed in the old server.pl but had
|
// The following roots existed in the old server.pl but had
|
||||||
// no profile configurations and were thus unusable
|
// no profile configurations and were thus unusable
|
||||||
//
|
//
|
||||||
// X509RootClass3s: &x509_ops.Root{Name: "class3s"}, // no profile configs
|
// X509RootClass3s: &x509ops.Root{Name: "class3s"}, // no profile configs
|
||||||
// X509Root3: &x509_ops.Root{Name: "root3"},
|
// X509Root3: &x509ops.Root{Name: "root3"},
|
||||||
// X509Root4: &x509_ops.Root{Name: "root4"},
|
// X509Root4: &x509ops.Root{Name: "root4"},
|
||||||
// X509Root5: &x509_ops.Root{Name: "root5"},
|
// X509Root5: &x509ops.Root{Name: "root5"},
|
||||||
},
|
},
|
||||||
Profiles: map[shared.CertificateProfileId]interface{}{
|
Profiles: map[shared.CertificateProfileID]interface{}{
|
||||||
X509ProfileClient: x509_ops.NewProfile(
|
X509ProfileClient: x509ops.NewProfile(
|
||||||
"client",
|
"client",
|
||||||
clientPrototype,
|
clientPrototype,
|
||||||
[]x509_ops.SubjectDnField{
|
[]x509ops.SubjectDnField{
|
||||||
x509_ops.SubjectDnFieldCommonName,
|
x509ops.SubjectDnFieldCommonName,
|
||||||
x509_ops.SubjectDnFieldEmailAddress,
|
x509ops.SubjectDnFieldEmailAddress,
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
X509ProfileClientOrg: x509_ops.NewProfile("client-org", clientPrototype,
|
X509ProfileClientOrg: x509ops.NewProfile("client-org", clientPrototype,
|
||||||
[]x509_ops.SubjectDnField{
|
[]x509ops.SubjectDnField{
|
||||||
x509_ops.SubjectDnFieldCountryName,
|
x509ops.SubjectDnFieldCountryName,
|
||||||
x509_ops.SubjectDnFieldStateOrProvinceName,
|
x509ops.SubjectDnFieldStateOrProvinceName,
|
||||||
x509_ops.SubjectDnFieldLocalityName,
|
x509ops.SubjectDnFieldLocalityName,
|
||||||
x509_ops.SubjectDnFieldOrganizationName,
|
x509ops.SubjectDnFieldOrganizationName,
|
||||||
x509_ops.SubjectDnFieldOrganizationalUnitName,
|
x509ops.SubjectDnFieldOrganizationalUnitName,
|
||||||
x509_ops.SubjectDnFieldCommonName,
|
x509ops.SubjectDnFieldCommonName,
|
||||||
x509_ops.SubjectDnFieldEmailAddress,
|
x509ops.SubjectDnFieldEmailAddress,
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
X509ProfileClientCodesign: x509_ops.NewProfile("client-codesign", codeSignPrototype,
|
X509ProfileClientCodesign: x509ops.NewProfile("client-codesign", codeSignPrototype,
|
||||||
[]x509_ops.SubjectDnField{
|
[]x509ops.SubjectDnField{
|
||||||
x509_ops.SubjectDnFieldCountryName,
|
x509ops.SubjectDnFieldCountryName,
|
||||||
x509_ops.SubjectDnFieldStateOrProvinceName,
|
x509ops.SubjectDnFieldStateOrProvinceName,
|
||||||
x509_ops.SubjectDnFieldLocalityName,
|
x509ops.SubjectDnFieldLocalityName,
|
||||||
x509_ops.SubjectDnFieldCommonName,
|
x509ops.SubjectDnFieldCommonName,
|
||||||
x509_ops.SubjectDnFieldEmailAddress,
|
x509ops.SubjectDnFieldEmailAddress,
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
// X509ProfileClientMachine: &x509_ops.Profile{Name: "client-machine"},
|
// X509ProfileClientMachine: &x509ops.Profile{Name: "client-machine"},
|
||||||
// X509ProfileClientAds: &x509_ops.Profile{Name: "client-ads"},
|
// X509ProfileClientAds: &x509ops.Profile{Name: "client-ads"},
|
||||||
X509ProfileServer: x509_ops.NewProfile("server", serverPrototype,
|
X509ProfileServer: x509ops.NewProfile("server", serverPrototype,
|
||||||
[]x509_ops.SubjectDnField{
|
[]x509ops.SubjectDnField{
|
||||||
x509_ops.SubjectDnFieldCommonName,
|
x509ops.SubjectDnFieldCommonName,
|
||||||
},
|
},
|
||||||
[]x509_ops.AltNameType{x509_ops.NameTypeDNS, x509_ops.NameTypeXmppJid},
|
[]x509ops.AltNameType{x509ops.NameTypeDNS, x509ops.NameTypeXMPPJid},
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
X509ProfileServerOrg: x509_ops.NewProfile("server-org", serverPrototype,
|
X509ProfileServerOrg: x509ops.NewProfile("server-org", serverPrototype,
|
||||||
[]x509_ops.SubjectDnField{
|
[]x509ops.SubjectDnField{
|
||||||
x509_ops.SubjectDnFieldCountryName,
|
x509ops.SubjectDnFieldCountryName,
|
||||||
x509_ops.SubjectDnFieldStateOrProvinceName,
|
x509ops.SubjectDnFieldStateOrProvinceName,
|
||||||
x509_ops.SubjectDnFieldLocalityName,
|
x509ops.SubjectDnFieldLocalityName,
|
||||||
x509_ops.SubjectDnFieldOrganizationName,
|
x509ops.SubjectDnFieldOrganizationName,
|
||||||
x509_ops.SubjectDnFieldOrganizationalUnitName,
|
x509ops.SubjectDnFieldOrganizationalUnitName,
|
||||||
x509_ops.SubjectDnFieldCommonName,
|
x509ops.SubjectDnFieldCommonName,
|
||||||
},
|
},
|
||||||
[]x509_ops.AltNameType{x509_ops.NameTypeDNS, x509_ops.NameTypeXmppJid},
|
[]x509ops.AltNameType{x509ops.NameTypeDNS, x509ops.NameTypeXMPPJid},
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
// X509ProfileServerJabber: &x509_ops.Profile{Name: "server-jabber"},
|
// X509ProfileServerJabber: &x509ops.Profile{Name: "server-jabber"},
|
||||||
X509ProfileOCSP: x509_ops.NewProfile("ocsp", ocspPrototype,
|
X509ProfileOCSP: x509ops.NewProfile("ocsp", ocspPrototype,
|
||||||
[]x509_ops.SubjectDnField{
|
[]x509ops.SubjectDnField{
|
||||||
x509_ops.SubjectDnFieldCountryName,
|
x509ops.SubjectDnFieldCountryName,
|
||||||
x509_ops.SubjectDnFieldStateOrProvinceName,
|
x509ops.SubjectDnFieldStateOrProvinceName,
|
||||||
x509_ops.SubjectDnFieldLocalityName,
|
x509ops.SubjectDnFieldLocalityName,
|
||||||
x509_ops.SubjectDnFieldOrganizationName,
|
x509ops.SubjectDnFieldOrganizationName,
|
||||||
x509_ops.SubjectDnFieldOrganizationalUnitName,
|
x509ops.SubjectDnFieldOrganizationalUnitName,
|
||||||
x509_ops.SubjectDnFieldCommonName,
|
x509ops.SubjectDnFieldCommonName,
|
||||||
x509_ops.SubjectDnFieldEmailAddress,
|
x509ops.SubjectDnFieldEmailAddress,
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
X509ProfileTimestamp: x509_ops.NewProfile("timestamp", timestampPrototype,
|
X509ProfileTimestamp: x509ops.NewProfile("timestamp", timestampPrototype,
|
||||||
[]x509_ops.SubjectDnField{
|
[]x509ops.SubjectDnField{
|
||||||
x509_ops.SubjectDnFieldCountryName,
|
x509ops.SubjectDnFieldCountryName,
|
||||||
x509_ops.SubjectDnFieldStateOrProvinceName,
|
x509ops.SubjectDnFieldStateOrProvinceName,
|
||||||
x509_ops.SubjectDnFieldLocalityName,
|
x509ops.SubjectDnFieldLocalityName,
|
||||||
x509_ops.SubjectDnFieldOrganizationName,
|
x509ops.SubjectDnFieldOrganizationName,
|
||||||
x509_ops.SubjectDnFieldOrganizationalUnitName,
|
x509ops.SubjectDnFieldOrganizationalUnitName,
|
||||||
x509_ops.SubjectDnFieldCommonName,
|
x509ops.SubjectDnFieldCommonName,
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
// X509ProfileProxy: &x509_ops.Profile{Name: "proxy"},
|
// X509ProfileProxy: &x509ops.Profile{Name: "proxy"},
|
||||||
// X509ProfileSubCA: &x509_ops.Profile{Name: "subca"},
|
// X509ProfileSubCA: &x509ops.Profile{Name: "subca"},
|
||||||
},
|
},
|
||||||
// constants for openssl invocations. Should be replaced with
|
// constants for openssl invocations. Should be replaced with
|
||||||
// something more useful
|
// something more useful
|
||||||
DigestAlgorithms: map[shared.MessageDigestAlgorithmId]interface{}{
|
DigestAlgorithms: map[shared.SignatureAlgorithmID]interface{}{
|
||||||
X509MDDefault: x509.SHA256WithRSA,
|
X509MDDefault: x509.SHA256WithRSA,
|
||||||
X509MDMd5: x509.MD5WithRSA,
|
X509MDMd5: x509.MD5WithRSA,
|
||||||
X509MDSha1: x509.SHA1WithRSA,
|
X509MDSha1: x509.SHA1WithRSA,
|
||||||
|
@ -251,8 +251,8 @@ func NewCommandProcessor() *CommandProcessor {
|
||||||
},
|
},
|
||||||
CsOpenPGP: {
|
CsOpenPGP: {
|
||||||
Name: "OpenPGP",
|
Name: "OpenPGP",
|
||||||
Roots: map[shared.CryptoSystemRootId]interface{}{
|
Roots: map[shared.CryptoSystemRootID]interface{}{
|
||||||
OpenPGPRoot0: &openpgp_ops.OpenPGPRoot{
|
OpenPGPRoot0: &openpgpops.OpenPGPRoot{
|
||||||
Name: "OpenPGP Root",
|
Name: "OpenPGP Root",
|
||||||
SecretKeyRing: path.Join(
|
SecretKeyRing: path.Join(
|
||||||
settings.OpenPGPKeyRingDir,
|
settings.OpenPGPKeyRingDir,
|
||||||
|
@ -262,12 +262,12 @@ func NewCommandProcessor() *CommandProcessor {
|
||||||
Identifier: settings.OpenPGPUidEmail,
|
Identifier: settings.OpenPGPUidEmail,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Profiles: map[shared.CertificateProfileId]interface{}{
|
Profiles: map[shared.CertificateProfileID]interface{}{
|
||||||
OpenPGPDefaultProfile: &openpgp_ops.OpenPGPProfile{Name: "default"},
|
OpenPGPDefaultProfile: &openpgpops.OpenPGPProfile{Name: "default"},
|
||||||
},
|
},
|
||||||
// constants for gnupg cert-digest-algo parameter. Should be replaced with
|
// constants for gnupg cert-digest-algo parameter. Should be replaced with
|
||||||
// something more useful
|
// something more useful
|
||||||
DigestAlgorithms: map[shared.MessageDigestAlgorithmId]interface{}{
|
DigestAlgorithms: map[shared.SignatureAlgorithmID]interface{}{
|
||||||
OpenPGPDefaultMD: crypto.SHA256,
|
OpenPGPDefaultMD: crypto.SHA256,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package x509_ops
|
package x509ops
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
@ -6,7 +6,7 @@ import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha1"
|
"crypto/sha1" // #nosec G505 needed for protocol version 1
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
@ -27,11 +27,16 @@ import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"git.cacert.org/cacert-gosigner/shared"
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
"git.cacert.org/cacert-gosigner/signer/common"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const crlLifetime = time.Hour * 24 * 7
|
const crlLifetime = time.Hour * 24 * 7
|
||||||
|
|
||||||
|
const (
|
||||||
|
pemTypeCertificate = "CERTIFICATE"
|
||||||
|
pemTypeCertificateRequest = "CERTIFICATE REQUEST"
|
||||||
|
pemTypePrivateKey = "PRIVATE KEY"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
oidPkcs9EmailAddress = []int{1, 2, 840, 113549, 1, 9, 1}
|
oidPkcs9EmailAddress = []int{1, 2, 840, 113549, 1, 9, 1}
|
||||||
)
|
)
|
||||||
|
@ -57,27 +62,30 @@ func loadCertificate(certificateFile string) (*x509.Certificate, error) {
|
||||||
pemBytes, err := ioutil.ReadFile(certificateFile)
|
pemBytes, err := ioutil.ReadFile(certificateFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"could not load certificate %s: %v",
|
"could not load certificate %s: %w",
|
||||||
certificateFile,
|
certificateFile,
|
||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pemBlock, _ := pem.Decode(pemBytes)
|
pemBlock, _ := pem.Decode(pemBytes)
|
||||||
if pemBlock.Type != "CERTIFICATE" {
|
if pemBlock.Type != pemTypeCertificate {
|
||||||
log.Warnf(
|
log.Warnf(
|
||||||
"PEM in %s is probably not a certificate. PEM block has type %s",
|
"PEM in %s is probably not a certificate. PEM block has type %s",
|
||||||
certificateFile,
|
certificateFile,
|
||||||
pemBlock.Type,
|
pemBlock.Type,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
certificate, err := x509.ParseCertificate(pemBlock.Bytes)
|
certificate, err := x509.ParseCertificate(pemBlock.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"could no parse certificate from %s: %v",
|
"could no parse certificate from %s: %w",
|
||||||
certificateFile,
|
certificateFile,
|
||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return certificate, nil
|
return certificate, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,30 +93,34 @@ func loadPrivateKey(filename string) (crypto.Signer, error) {
|
||||||
pemBytes, err := ioutil.ReadFile(filename)
|
pemBytes, err := ioutil.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"could not load private key %s: %v",
|
"could not load private key %s: %w",
|
||||||
filename,
|
filename,
|
||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pemBlock, _ := pem.Decode(pemBytes)
|
pemBlock, _ := pem.Decode(pemBytes)
|
||||||
if pemBlock == nil {
|
if pemBlock == nil {
|
||||||
return nil, fmt.Errorf("no PEM data found in %s", filename)
|
return nil, fmt.Errorf("no PEM data found in %s", filename)
|
||||||
}
|
}
|
||||||
if pemBlock.Type != "PRIVATE KEY" {
|
|
||||||
|
if pemBlock.Type != pemTypePrivateKey {
|
||||||
log.Warnf(
|
log.Warnf(
|
||||||
"PEM in %s is probably not a private key. PEM block has type %s",
|
"PEM in %s is probably not a private key. PEM block has type %s",
|
||||||
filename,
|
filename,
|
||||||
pemBlock.Type,
|
pemBlock.Type,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
privateKey, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes)
|
privateKey, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"could no parse private key from %s: %v",
|
"could no parse private key from %s: %w",
|
||||||
filename,
|
filename,
|
||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return privateKey.(*rsa.PrivateKey), nil
|
return privateKey.(*rsa.PrivateKey), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,19 +128,24 @@ func (x *Root) getNextSerialNumber() (*big.Int, error) {
|
||||||
// TODO: decide whether we should use 64 bit random serial numbers as
|
// TODO: decide whether we should use 64 bit random serial numbers as
|
||||||
// recommended by CAB forum baseline requirements
|
// recommended by CAB forum baseline requirements
|
||||||
serialNumberFile := x.serialNumberFile
|
serialNumberFile := x.serialNumberFile
|
||||||
|
|
||||||
_, err := os.Stat(serialNumberFile)
|
_, err := os.Stat(serialNumberFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("serial number file %s does not exist: %v", x.serialNumberFile, err)
|
log.Warnf("serial number file %s does not exist: %v", x.serialNumberFile, err)
|
||||||
|
|
||||||
return big.NewInt(1), nil
|
return big.NewInt(1), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(x.serialNumberFile)
|
data, err := ioutil.ReadFile(x.serialNumberFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not read serial number file %s: %v", x.serialNumberFile, err)
|
return nil, fmt.Errorf("could not read serial number file %s: %w", x.serialNumberFile, err)
|
||||||
}
|
}
|
||||||
result, err := common.StringAsBigInt(data)
|
|
||||||
|
result, err := stringAsBigInt(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not parse content of %s as serial number: %v", x.serialNumberFile, err)
|
return nil, fmt.Errorf("could not parse content of %s as serial number: %w", x.serialNumberFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,26 +153,32 @@ func (x *Root) getNextCRLNumber() (*big.Int, error) {
|
||||||
_, err := os.Stat(x.crlNumberFile)
|
_, err := os.Stat(x.crlNumberFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("CRL number file %s does not exist: %v", x.crlNumberFile, err)
|
log.Warnf("CRL number file %s does not exist: %v", x.crlNumberFile, err)
|
||||||
|
|
||||||
return big.NewInt(1), nil
|
return big.NewInt(1), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(x.crlNumberFile)
|
data, err := ioutil.ReadFile(x.crlNumberFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not read CRL number file %s: %v", x.crlNumberFile, err)
|
return nil, fmt.Errorf("could not read CRL number file %s: %w", x.crlNumberFile, err)
|
||||||
}
|
}
|
||||||
result, err := common.StringAsBigInt(data)
|
|
||||||
|
result, err := stringAsBigInt(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not parse content of %s as CRL number: %v", x.crlNumberFile, err)
|
return nil, fmt.Errorf("could not parse content of %s as CRL number: %w", x.crlNumberFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Root) bumpCRLNumber(current *big.Int) error {
|
func (x *Root) bumpCRLNumber(current *big.Int) error {
|
||||||
serial := current.Int64() + 1
|
serial := current.Int64() + 1
|
||||||
crlNumberFile := x.crlNumberFile
|
crlNumberFile := x.crlNumberFile
|
||||||
|
|
||||||
outFile, err := ioutil.TempFile(path.Dir(crlNumberFile), "*.txt")
|
outFile, err := ioutil.TempFile(path.Dir(crlNumberFile), "*.txt")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not create temporary crl number file: %v", err)
|
return fmt.Errorf("could not create temporary crl number file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() { _ = outFile.Close() }()
|
defer func() { _ = outFile.Close() }()
|
||||||
|
|
||||||
_, err = outFile.WriteString(fmt.Sprintf(
|
_, err = outFile.WriteString(fmt.Sprintf(
|
||||||
|
@ -163,27 +186,32 @@ func (x *Root) bumpCRLNumber(current *big.Int) error {
|
||||||
strings.ToUpper(strconv.FormatInt(serial, 16)),
|
strings.ToUpper(strconv.FormatInt(serial, 16)),
|
||||||
))
|
))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not write new CRL number %d to %s: %v", serial, outFile.Name(), err)
|
return fmt.Errorf("could not write new CRL number %d to %s: %w", serial, outFile.Name(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = outFile.Close(); err != nil {
|
if err = outFile.Close(); err != nil {
|
||||||
return fmt.Errorf("could not close temporary file %s: %v", outFile.Name(), err)
|
return fmt.Errorf("could not close temporary file %s: %w", outFile.Name(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = os.Rename(crlNumberFile, fmt.Sprintf("%s.old", crlNumberFile)); err != nil {
|
if err = os.Rename(crlNumberFile, fmt.Sprintf("%s.old", crlNumberFile)); err != nil {
|
||||||
return fmt.Errorf("could not rename %s to %s.old: %v", crlNumberFile, crlNumberFile, err)
|
return fmt.Errorf("could not rename %s to %s.old: %w", crlNumberFile, crlNumberFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = os.Rename(outFile.Name(), crlNumberFile); err != nil {
|
if err = os.Rename(outFile.Name(), crlNumberFile); err != nil {
|
||||||
return fmt.Errorf("could not rename %s to %s: %v", outFile.Name(), crlNumberFile, err)
|
return fmt.Errorf("could not rename %s to %s: %w", outFile.Name(), crlNumberFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Root) bumpSerialNumber(current *big.Int) error {
|
func (x *Root) bumpSerialNumber(current *big.Int) error {
|
||||||
serial := current.Int64() + 1
|
serial := current.Int64() + 1
|
||||||
|
|
||||||
outFile, err := ioutil.TempFile(path.Dir(x.serialNumberFile), "*.txt")
|
outFile, err := ioutil.TempFile(path.Dir(x.serialNumberFile), "*.txt")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not open temporary serial number file: %v", err)
|
return fmt.Errorf("could not open temporary serial number file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() { _ = outFile.Close() }()
|
defer func() { _ = outFile.Close() }()
|
||||||
|
|
||||||
_, err = outFile.WriteString(fmt.Sprintf(
|
_, err = outFile.WriteString(fmt.Sprintf(
|
||||||
|
@ -191,49 +219,59 @@ func (x *Root) bumpSerialNumber(current *big.Int) error {
|
||||||
strings.ToUpper(strconv.FormatInt(serial, 16)),
|
strings.ToUpper(strconv.FormatInt(serial, 16)),
|
||||||
))
|
))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not write new serial number %d to %s: %v", serial, outFile.Name(), err)
|
return fmt.Errorf("could not write new serial number %d to %s: %w", serial, outFile.Name(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = outFile.Close(); err != nil {
|
if err = outFile.Close(); err != nil {
|
||||||
return fmt.Errorf("could not close temporary file %s: %v", outFile.Name(), err)
|
return fmt.Errorf("could not close temporary file %s: %w", outFile.Name(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = os.Stat(x.serialNumberFile); err == nil {
|
if _, err = os.Stat(x.serialNumberFile); err == nil {
|
||||||
if err = os.Rename(x.serialNumberFile, fmt.Sprintf("%s.old", x.serialNumberFile)); err != nil {
|
if err = os.Rename(x.serialNumberFile, fmt.Sprintf("%s.old", x.serialNumberFile)); err != nil {
|
||||||
return fmt.Errorf("could not rename %s to %s.old: %v", x.serialNumberFile, x.serialNumberFile, err)
|
return fmt.Errorf("could not rename %s to %s.old: %w", x.serialNumberFile, x.serialNumberFile, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = os.Rename(outFile.Name(), x.serialNumberFile); err != nil {
|
if err = os.Rename(outFile.Name(), x.serialNumberFile); err != nil {
|
||||||
return fmt.Errorf("could not rename %s to %s: %v", outFile.Name(), x.serialNumberFile, err)
|
return fmt.Errorf("could not rename %s to %s: %w", outFile.Name(), x.serialNumberFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Root) loadRevokedCertificatesFromDatabase() ([]pkix.RevokedCertificate, error) {
|
func (x *Root) loadRevokedCertificatesFromDatabase() ([]pkix.RevokedCertificate, error) {
|
||||||
databaseFile := x.databaseFile
|
databaseFile := x.databaseFile
|
||||||
|
|
||||||
_, err := os.Stat(databaseFile)
|
_, err := os.Stat(databaseFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("openssl certificate database file %s does not exist: %v", databaseFile, err)
|
log.Warnf("openssl certificate database file %s does not exist: %v", databaseFile, err)
|
||||||
|
|
||||||
return []pkix.RevokedCertificate{}, nil
|
return []pkix.RevokedCertificate{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.Open(databaseFile)
|
file, err := os.Open(databaseFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not open openssl certificate database file %s: %v", databaseFile, err)
|
return nil, fmt.Errorf("could not open openssl certificate database file %s: %w", databaseFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() { _ = file.Close() }()
|
defer func() { _ = file.Close() }()
|
||||||
|
|
||||||
result := make([]pkix.RevokedCertificate, 0)
|
result := make([]pkix.RevokedCertificate, 0)
|
||||||
|
|
||||||
scanner := bufio.NewScanner(file)
|
scanner := bufio.NewScanner(file)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
line := strings.Split(scanner.Text(), "\t")
|
line := strings.Split(scanner.Text(), "\t")
|
||||||
if line[0] == "R" {
|
if line[0] == "R" {
|
||||||
serialNumber, err := common.StringAsBigInt([]byte(line[3]))
|
serialNumber, err := stringAsBigInt([]byte(line[3]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not parse serial number %s as big int: %v", line[3], err)
|
return nil, fmt.Errorf("could not parse serial number %s as big int: %w", line[3], err)
|
||||||
}
|
}
|
||||||
|
|
||||||
revokeTs, err := strconv.ParseInt(line[2][:len(line[2])-1], 10, 64)
|
revokeTs, err := strconv.ParseInt(line[2][:len(line[2])-1], 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not parse serial number: %v", err)
|
return nil, fmt.Errorf("could not parse serial number: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
result = append(result, pkix.RevokedCertificate{
|
result = append(result, pkix.RevokedCertificate{
|
||||||
SerialNumber: serialNumber,
|
SerialNumber: serialNumber,
|
||||||
RevocationTime: time.Unix(revokeTs, 0),
|
RevocationTime: time.Unix(revokeTs, 0),
|
||||||
|
@ -241,24 +279,29 @@ func (x *Root) loadRevokedCertificatesFromDatabase() ([]pkix.RevokedCertificate,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Root) recordRevocation(certificate *x509.Certificate) (*pkix.RevokedCertificate, error) {
|
func (x *Root) recordRevocation(certificate *x509.Certificate) (*pkix.RevokedCertificate, error) {
|
||||||
_, err := os.Stat(x.databaseFile)
|
_, err := os.Stat(x.databaseFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("openssl certificate database file %s does not exist: %v", x.databaseFile, err)
|
return nil, fmt.Errorf("openssl certificate database file %s does not exist: %w", x.databaseFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
inFile, err := os.Open(x.databaseFile)
|
inFile, err := os.Open(x.databaseFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not open openssl certificate database file %s: %v", x.databaseFile, err)
|
return nil, fmt.Errorf("could not open openssl certificate database file %s: %w", x.databaseFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() { _ = inFile.Close() }()
|
defer func() { _ = inFile.Close() }()
|
||||||
|
|
||||||
outFile, err := ioutil.TempFile(path.Dir(x.databaseFile), "*.txt")
|
outFile, err := ioutil.TempFile(path.Dir(x.databaseFile), "*.txt")
|
||||||
|
|
||||||
defer func() { _ = outFile.Close() }()
|
defer func() { _ = outFile.Close() }()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not open temporary database file: %v", err)
|
return nil, fmt.Errorf("could not open temporary database file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
scanner := bufio.NewScanner(inFile)
|
scanner := bufio.NewScanner(inFile)
|
||||||
|
@ -270,10 +313,12 @@ func (x *Root) recordRevocation(certificate *x509.Certificate) (*pkix.RevokedCer
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
line := scanner.Text()
|
line := scanner.Text()
|
||||||
parts := strings.Split(line, "\t")
|
parts := strings.Split(line, "\t")
|
||||||
serialNumber, err := common.StringAsBigInt([]byte(parts[3]))
|
|
||||||
|
serialNumber, err := stringAsBigInt([]byte(parts[3]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not parse serial number %s as big int: %v", parts[3], err)
|
return nil, fmt.Errorf("could not parse serial number %s as big int: %w", parts[3], err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if serialNumber == certificate.SerialNumber {
|
if serialNumber == certificate.SerialNumber {
|
||||||
line = strings.Join(
|
line = strings.Join(
|
||||||
[]string{"R", parts[1], strconv.FormatInt(revocationTime.Unix(), 10) + "Z", parts[3], parts[4]},
|
[]string{"R", parts[1], strconv.FormatInt(revocationTime.Unix(), 10) + "Z", parts[3], parts[4]},
|
||||||
|
@ -281,24 +326,26 @@ func (x *Root) recordRevocation(certificate *x509.Certificate) (*pkix.RevokedCer
|
||||||
)
|
)
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = writer.WriteString(fmt.Sprintf("%s\n", line)); err != nil {
|
if _, err = writer.WriteString(fmt.Sprintf("%s\n", line)); err != nil {
|
||||||
return nil, fmt.Errorf("could not write '%s' to %s: %v", line, outFile.Name(), err)
|
return nil, fmt.Errorf("could not write '%s' to %s: %w", line, outFile.Name(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = outFile.Close(); err != nil {
|
if err = outFile.Close(); err != nil {
|
||||||
return nil, fmt.Errorf("could not close temporary file %s: %v", outFile.Name(), err)
|
return nil, fmt.Errorf("could not close temporary file %s: %w", outFile.Name(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = inFile.Close(); err != nil {
|
if err = inFile.Close(); err != nil {
|
||||||
return nil, fmt.Errorf("could not close %s: %v", x.databaseFile, err)
|
return nil, fmt.Errorf("could not close %s: %w", x.databaseFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = os.Rename(x.databaseFile, fmt.Sprintf("%s.old", x.databaseFile)); err != nil {
|
if err = os.Rename(x.databaseFile, fmt.Sprintf("%s.old", x.databaseFile)); err != nil {
|
||||||
return nil, fmt.Errorf("could not rename %s to %s.old: %v", x.databaseFile, x.databaseFile, err)
|
return nil, fmt.Errorf("could not rename %s to %s.old: %w", x.databaseFile, x.databaseFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = os.Rename(outFile.Name(), x.databaseFile); err != nil {
|
if err = os.Rename(outFile.Name(), x.databaseFile); err != nil {
|
||||||
return nil, fmt.Errorf("could not rename temporary file %s to %s: %v", outFile.Name(), x.databaseFile, err)
|
return nil, fmt.Errorf("could not rename temporary file %s to %s: %w", outFile.Name(), x.databaseFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
|
@ -313,22 +360,22 @@ func (x *Root) recordRevocation(certificate *x509.Certificate) (*pkix.RevokedCer
|
||||||
|
|
||||||
func (x *Root) RevokeCertificate(request []byte) (*pkix.RevokedCertificate, error) {
|
func (x *Root) RevokeCertificate(request []byte) (*pkix.RevokedCertificate, error) {
|
||||||
pemBlock, _ := pem.Decode(request)
|
pemBlock, _ := pem.Decode(request)
|
||||||
if pemBlock.Type != "CERTIFICATE" {
|
if pemBlock.Type != pemTypeCertificate {
|
||||||
if pemBlock.Type != "CERTIFICATE" {
|
|
||||||
log.Warnf(
|
log.Warnf(
|
||||||
"PEM structure is probably not a certificate. PEM block has type %s",
|
"PEM structure is probably not a certificate. PEM block has type %s",
|
||||||
pemBlock.Type,
|
pemBlock.Type,
|
||||||
)
|
)
|
||||||
log.Trace(request)
|
log.Trace(request)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
certificate, err := x509.ParseCertificate(pemBlock.Bytes)
|
certificate, err := x509.ParseCertificate(pemBlock.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"could no parse certificate: %v",
|
"could no parse certificate: %w",
|
||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return x.recordRevocation(certificate)
|
return x.recordRevocation(certificate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,10 +384,12 @@ func (x *Root) GenerateCrl(algorithm x509.SignatureAlgorithm) ([]byte, *[20]byte
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
nextCrlNumber, err := x.getNextCRLNumber()
|
nextCrlNumber, err := x.getNextCRLNumber()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
crlTemplate := &x509.RevocationList{
|
crlTemplate := &x509.RevocationList{
|
||||||
SignatureAlgorithm: algorithm,
|
SignatureAlgorithm: algorithm,
|
||||||
RevokedCertificates: certificatesToRevoke,
|
RevokedCertificates: certificatesToRevoke,
|
||||||
|
@ -363,20 +412,23 @@ func (x *Root) GenerateCrl(algorithm x509.SignatureAlgorithm) ([]byte, *[20]byte
|
||||||
x.privateKey,
|
x.privateKey,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("could not create new CRL: %v", err)
|
return nil, nil, fmt.Errorf("could not create new CRL: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = ioutil.WriteFile(x.crlFileName, crlBytes, 0644); err != nil {
|
if err = ioutil.WriteFile(x.crlFileName, crlBytes, 0600); err != nil {
|
||||||
return nil, nil, fmt.Errorf("could not write new CRL to %s: %v", x.crlFileName, err)
|
return nil, nil, fmt.Errorf("could not write new CRL to %s: %w", x.crlFileName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sha1 is implied by protocol version 1
|
||||||
|
// #nosec G401
|
||||||
newCrlHash := sha1.Sum(crlBytes)
|
newCrlHash := sha1.Sum(crlBytes)
|
||||||
hashedCrlFileName := path.Join(
|
hashedCrlFileName := path.Join(
|
||||||
x.crlHashDir,
|
x.crlHashDir,
|
||||||
fmt.Sprintf("%s.crl", hex.EncodeToString(newCrlHash[:])),
|
fmt.Sprintf("%s.crl", hex.EncodeToString(newCrlHash[:])),
|
||||||
)
|
)
|
||||||
if err = ioutil.WriteFile(hashedCrlFileName, crlBytes, 0644); err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("could not write new CRL to %s: %v", hashedCrlFileName, err)
|
if err = ioutil.WriteFile(hashedCrlFileName, crlBytes, 0600); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("could not write new CRL to %s: %w", hashedCrlFileName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return crlBytes, &newCrlHash, nil
|
return crlBytes, &newCrlHash, nil
|
||||||
|
@ -388,6 +440,7 @@ func (x *Root) DeleteOldCRLs(keepHashes ...string) error {
|
||||||
found, err := filepath.Glob(path.Join(x.crlHashDir, "*.crl"))
|
found, err := filepath.Glob(path.Join(x.crlHashDir, "*.crl"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("could not match files: %v", err)
|
log.Warnf("could not match files: %v", err)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,7 +452,7 @@ nextFound:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := os.Remove(filename); err != nil {
|
if err := os.Remove(filename); err != nil {
|
||||||
return fmt.Errorf("could not delete %s: %v", filename, err)
|
return fmt.Errorf("could not delete %s: %w", filename, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,6 +474,7 @@ func (x *Root) checkPreconditions() {
|
||||||
for _, success := range results {
|
for _, success := range results {
|
||||||
if !success {
|
if !success {
|
||||||
log.Warnf("preconditions for %s failed, operations may fail too", x)
|
log.Warnf("preconditions for %s failed, operations may fail too", x)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -428,26 +482,34 @@ func (x *Root) checkPreconditions() {
|
||||||
|
|
||||||
func (x *Root) checkFile(path, prefix string) bool {
|
func (x *Root) checkFile(path, prefix string) bool {
|
||||||
ok := true
|
ok := true
|
||||||
|
|
||||||
if s, e := os.Stat(path); e != nil {
|
if s, e := os.Stat(path); e != nil {
|
||||||
log.Warnf("%s file %s of %s has issues: %v", prefix, path, x, e)
|
log.Warnf("%s file %s of %s has issues: %v", prefix, path, x, e)
|
||||||
|
|
||||||
ok = false
|
ok = false
|
||||||
} else if s.IsDir() {
|
} else if s.IsDir() {
|
||||||
log.Warnf("%s file %s of %s is a directory", prefix, path, x)
|
log.Warnf("%s file %s of %s is a directory", prefix, path, x)
|
||||||
|
|
||||||
ok = false
|
ok = false
|
||||||
}
|
}
|
||||||
|
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Root) checkDir(path, prefix string) bool {
|
func (x *Root) checkDir(path, prefix string) bool {
|
||||||
ok := true
|
ok := true
|
||||||
|
|
||||||
if s, e := os.Stat(path); e != nil {
|
if s, e := os.Stat(path); e != nil {
|
||||||
log.Warnf("%s %s of %s has issues: %v", prefix, path, x, e)
|
log.Warnf("%s %s of %s has issues: %v", prefix, path, x, e)
|
||||||
|
|
||||||
if err := os.MkdirAll(path, 0755); err != nil {
|
if err := os.MkdirAll(path, 0755); err != nil {
|
||||||
log.Warnf("could not create %s %s of %s: %v", prefix, path, x, err)
|
log.Warnf("could not create %s %s of %s: %v", prefix, path, x, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = false
|
ok = false
|
||||||
} else if !s.IsDir() {
|
} else if !s.IsDir() {
|
||||||
log.Warnf("%s %s of %s is not a directory", prefix, path, x)
|
log.Warnf("%s %s of %s is not a directory", prefix, path, x)
|
||||||
|
|
||||||
ok = false
|
ok = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,35 +530,45 @@ func (x *Root) SignCertificate(
|
||||||
params *SigningRequestParameters,
|
params *SigningRequestParameters,
|
||||||
) ([]byte, error) {
|
) ([]byte, error) {
|
||||||
var publicKey interface{}
|
var publicKey interface{}
|
||||||
|
|
||||||
|
// nolint:nestif
|
||||||
if params.IsSpkac {
|
if params.IsSpkac {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
const spkacPrefix = "SPKAC="
|
const spkacPrefix = "SPKAC="
|
||||||
|
|
||||||
if !bytes.Equal([]byte(spkacPrefix), params.Request[:len(spkacPrefix)]) {
|
if !bytes.Equal([]byte(spkacPrefix), params.Request[:len(spkacPrefix)]) {
|
||||||
return nil, fmt.Errorf("request does not contain a valid SPKAC string")
|
return nil, fmt.Errorf("request does not contain a valid SPKAC string")
|
||||||
}
|
}
|
||||||
|
|
||||||
derBytes, err := base64.StdEncoding.DecodeString(string(params.Request[len(spkacPrefix):]))
|
derBytes, err := base64.StdEncoding.DecodeString(string(params.Request[len(spkacPrefix):]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not decode SPKAC bytes: %v", err)
|
return nil, fmt.Errorf("could not decode SPKAC bytes: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
publicKey, err = pkac.ParseSPKAC(derBytes)
|
publicKey, err = pkac.ParseSPKAC(derBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not parse SPKAC: %v", err)
|
return nil, fmt.Errorf("could not parse SPKAC: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
csrBlock, _ := pem.Decode(params.Request)
|
csrBlock, _ := pem.Decode(params.Request)
|
||||||
if csrBlock.Type != "CERTIFICATE REQUEST" {
|
if csrBlock.Type != pemTypeCertificateRequest {
|
||||||
return nil, fmt.Errorf("unexpected PEM block '%s' instead of 'CERTIFICATE REQUEST'", csrBlock.Type)
|
return nil, fmt.Errorf(
|
||||||
|
"unexpected PEM block '%s' instead of '%s'",
|
||||||
|
csrBlock.Type,
|
||||||
|
pemTypeCertificateRequest,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
csr, err := x509.ParseCertificateRequest(csrBlock.Bytes)
|
csr, err := x509.ParseCertificateRequest(csrBlock.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not parse CSR: %v", err)
|
return nil, fmt.Errorf("could not parse CSR: %w", err)
|
||||||
}
|
}
|
||||||
publicKey = csr.PublicKey
|
publicKey = csr.PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
nextSerialNumber, err := x.getNextSerialNumber()
|
nextSerialNumber, err := x.getNextSerialNumber()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not get next serial number: %v", err)
|
return nil, fmt.Errorf("could not get next serial number: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy profile
|
// copy profile
|
||||||
|
@ -516,14 +588,15 @@ func (x *Root) SignCertificate(
|
||||||
// check subject
|
// check subject
|
||||||
subject, err := profile.parseSubject(params.Subject)
|
subject, err := profile.parseSubject(params.Subject)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not parse subject: %v", err)
|
return nil, fmt.Errorf("could not parse subject: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
certificate.Subject = *subject
|
certificate.Subject = *subject
|
||||||
|
|
||||||
// check altNames
|
// check altNames
|
||||||
err = profile.parseAltNames(certificate, params.SubjectAlternativeNames)
|
err = profile.parseAltNames(certificate, params.SubjectAlternativeNames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not parse subject alternative names: %v", err)
|
return nil, fmt.Errorf("could not parse subject alternative names: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
moveEmailsFromSubjectToAlternativeNames(certificate)
|
moveEmailsFromSubjectToAlternativeNames(certificate)
|
||||||
|
@ -532,24 +605,24 @@ func (x *Root) SignCertificate(
|
||||||
|
|
||||||
certBytes, err := x509.CreateCertificate(rand.Reader, certificate, x.certificate, publicKey, x.privateKey)
|
certBytes, err := x509.CreateCertificate(rand.Reader, certificate, x.certificate, publicKey, x.privateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not sign certificate: %v", err)
|
return nil, fmt.Errorf("could not sign certificate: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedCertificate, err := x509.ParseCertificate(certBytes)
|
parsedCertificate, err := x509.ParseCertificate(certBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not parse signed certificate: %v", err)
|
return nil, fmt.Errorf("could not parse signed certificate: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = x.bumpSerialNumber(nextSerialNumber); err != nil {
|
if err = x.bumpSerialNumber(nextSerialNumber); err != nil {
|
||||||
log.Errorf("could not bump serial number: %v", err)
|
return nil, fmt.Errorf("could not bump serial number: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = x.recordIssuedCertificate(parsedCertificate)
|
err = x.recordIssuedCertificate(parsedCertificate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not record signed certificate in database: %v", err)
|
return nil, fmt.Errorf("could not record signed certificate in database: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pemBytes := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes})
|
pemBytes := pem.EncodeToMemory(&pem.Block{Type: pemTypeCertificate, Bytes: certBytes})
|
||||||
log.Tracef("signed new certificate\n%s", pemBytes)
|
log.Tracef("signed new certificate\n%s", pemBytes)
|
||||||
|
|
||||||
return pemBytes, nil
|
return pemBytes, nil
|
||||||
|
@ -559,36 +632,46 @@ func (x *Root) SignCertificate(
|
||||||
// otherwise which is not compliant to RFC-5280
|
// otherwise which is not compliant to RFC-5280
|
||||||
func moveEmailsFromSubjectToAlternativeNames(certificate *x509.Certificate) {
|
func moveEmailsFromSubjectToAlternativeNames(certificate *x509.Certificate) {
|
||||||
extraNames := make([]pkix.AttributeTypeAndValue, 0)
|
extraNames := make([]pkix.AttributeTypeAndValue, 0)
|
||||||
|
|
||||||
for _, p := range certificate.Subject.ExtraNames {
|
for _, p := range certificate.Subject.ExtraNames {
|
||||||
if p.Type.Equal(oidPkcs9EmailAddress) {
|
if p.Type.Equal(oidPkcs9EmailAddress) {
|
||||||
email := p.Value.(string)
|
email := p.Value.(string)
|
||||||
|
|
||||||
if certificate.EmailAddresses == nil {
|
if certificate.EmailAddresses == nil {
|
||||||
certificate.EmailAddresses = []string{email}
|
certificate.EmailAddresses = []string{email}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, e := range certificate.EmailAddresses {
|
for _, e := range certificate.EmailAddresses {
|
||||||
if e == p.Value {
|
if e == p.Value {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
certificate.EmailAddresses = append(certificate.EmailAddresses, email)
|
certificate.EmailAddresses = append(certificate.EmailAddresses, email)
|
||||||
} else {
|
} else {
|
||||||
extraNames = append(extraNames, p)
|
extraNames = append(extraNames, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
certificate.Subject.ExtraNames = extraNames
|
certificate.Subject.ExtraNames = extraNames
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Root) recordIssuedCertificate(certificate *x509.Certificate) error {
|
func (x *Root) recordIssuedCertificate(certificate *x509.Certificate) error {
|
||||||
log.Tracef("recording %+v", certificate)
|
log.Tracef("recording %+v", certificate)
|
||||||
|
|
||||||
tempFile, err := ioutil.TempFile(path.Dir(x.databaseFile), "*.txt")
|
tempFile, err := ioutil.TempFile(path.Dir(x.databaseFile), "*.txt")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not create temporary file: %v", err)
|
return fmt.Errorf("could not create temporary file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() { _ = tempFile.Close() }()
|
defer func() { _ = tempFile.Close() }()
|
||||||
|
|
||||||
tempName := tempFile.Name()
|
tempName := tempFile.Name()
|
||||||
|
|
||||||
dbExists := false
|
dbExists := false
|
||||||
|
|
||||||
_, err = os.Stat(x.databaseFile)
|
_, err = os.Stat(x.databaseFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("openssl certificate database file %s does not exist: %v", x.databaseFile, err)
|
log.Warnf("openssl certificate database file %s does not exist: %v", x.databaseFile, err)
|
||||||
|
@ -597,76 +680,105 @@ func (x *Root) recordIssuedCertificate(certificate *x509.Certificate) error {
|
||||||
inFile, err := os.Open(x.databaseFile)
|
inFile, err := os.Open(x.databaseFile)
|
||||||
defer func() { _ = inFile.Close() }()
|
defer func() { _ = inFile.Close() }()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not open openssl certificate database file %s: %v", x.databaseFile, err)
|
return fmt.Errorf("could not open openssl certificate database file %s: %w", x.databaseFile, err)
|
||||||
}
|
}
|
||||||
_, err = io.Copy(tempFile, inFile)
|
_, err = io.Copy(tempFile, inFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not copy %s to temporary file %s: %v", x.databaseFile, tempName, err)
|
return fmt.Errorf("could not copy %s to temporary file %s: %w", x.databaseFile, tempName, err)
|
||||||
}
|
}
|
||||||
if err = inFile.Close(); err != nil {
|
if err = inFile.Close(); err != nil {
|
||||||
return fmt.Errorf("could not close %s: %v", x.databaseFile, err)
|
return fmt.Errorf("could not close %s: %w", x.databaseFile, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = tempFile.Close(); err != nil {
|
if err = tempFile.Close(); err != nil {
|
||||||
return fmt.Errorf("could not close temporary file %s: %v", tempName, err)
|
return fmt.Errorf("could not close temporary file %s: %w", tempName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
outFile, err := os.OpenFile(tempName, os.O_APPEND|os.O_WRONLY, 0644)
|
outFile, err := os.OpenFile(tempName, os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not open temporary file for writing %s: %v", tempName, err)
|
return fmt.Errorf("could not open temporary file for writing %s: %w", tempName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() { _ = outFile.Close() }()
|
defer func() { _ = outFile.Close() }()
|
||||||
line := strings.Join([]string{"V", strconv.FormatInt(certificate.NotBefore.Unix(), 10) + "Z", "", strings.ToUpper(certificate.SerialNumber.Text(16)), "unknown", opensslFormatDN(certificate.Subject)}, "\t")
|
|
||||||
|
line := strings.Join(
|
||||||
|
// nolint:gomnd
|
||||||
|
[]string{
|
||||||
|
"V",
|
||||||
|
strconv.FormatInt(certificate.NotBefore.Unix(), 10) + "Z",
|
||||||
|
"",
|
||||||
|
strings.ToUpper(certificate.SerialNumber.Text(16)),
|
||||||
|
"unknown",
|
||||||
|
opensslFormatDN(certificate.Subject),
|
||||||
|
},
|
||||||
|
"\t",
|
||||||
|
)
|
||||||
|
|
||||||
_, err = fmt.Fprintln(outFile, line)
|
_, err = fmt.Fprintln(outFile, line)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not write '%s' to %s: %v", line, tempName, err)
|
return fmt.Errorf("could not write '%s' to %s: %w", line, tempName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = outFile.Close(); err != nil {
|
if err = outFile.Close(); err != nil {
|
||||||
return fmt.Errorf("could not close temporary file %s: %v", tempName, err)
|
return fmt.Errorf("could not close temporary file %s: %w", tempName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if dbExists {
|
if dbExists {
|
||||||
if err = os.Rename(x.databaseFile, fmt.Sprintf("%s.old", x.databaseFile)); err != nil {
|
if err = os.Rename(x.databaseFile, fmt.Sprintf("%s.old", x.databaseFile)); err != nil {
|
||||||
return fmt.Errorf("could not rename %s to %s.old: %v", x.databaseFile, x.databaseFile, err)
|
return fmt.Errorf("could not rename %s to %s.old: %w", x.databaseFile, x.databaseFile, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = os.Rename(tempName, x.databaseFile); err != nil {
|
if err = os.Rename(tempName, x.databaseFile); err != nil {
|
||||||
return fmt.Errorf("could not rename temporary file %s to %s: %v", tempName, x.databaseFile, err)
|
return fmt.Errorf("could not rename temporary file %s to %s: %w", tempName, x.databaseFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opensslFormatDN(subject pkix.Name) string {
|
func opensslFormatDN(subject pkix.Name) string {
|
||||||
|
const (
|
||||||
|
oidSuffixCommonName = 3
|
||||||
|
oidSuffixCountryName = 6
|
||||||
|
oidSuffixLocalityName = 7
|
||||||
|
oidSuffixProvinceName = 8
|
||||||
|
oidSuffixOrganization = 10
|
||||||
|
oidSuffixOrganizationalUnit = 11
|
||||||
|
)
|
||||||
|
|
||||||
var buf strings.Builder
|
var buf strings.Builder
|
||||||
|
|
||||||
for _, rdn := range subject.ToRDNSequence() {
|
for _, rdn := range subject.ToRDNSequence() {
|
||||||
if len(rdn) == 0 {
|
if len(rdn) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, atv := range rdn {
|
for _, atv := range rdn {
|
||||||
value, ok := atv.Value.(string)
|
value, ok := atv.Value.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
t := atv.Type
|
t := atv.Type
|
||||||
if len(t) == 4 && t[:3].Equal([]int{2, 5, 4}) {
|
if len(t) == 4 && t[:3].Equal([]int{2, 5, 4}) {
|
||||||
switch t[3] {
|
switch t[3] {
|
||||||
case 3:
|
case oidSuffixCommonName:
|
||||||
buf.WriteString("/CN=")
|
buf.WriteString("/CN=")
|
||||||
buf.WriteString(value)
|
buf.WriteString(value)
|
||||||
case 6:
|
case oidSuffixCountryName:
|
||||||
buf.WriteString("/C=")
|
buf.WriteString("/C=")
|
||||||
buf.WriteString(value)
|
buf.WriteString(value)
|
||||||
case 7:
|
case oidSuffixLocalityName:
|
||||||
buf.WriteString("/L=")
|
buf.WriteString("/L=")
|
||||||
buf.WriteString(value)
|
buf.WriteString(value)
|
||||||
case 8:
|
case oidSuffixProvinceName:
|
||||||
buf.WriteString("/ST=")
|
buf.WriteString("/ST=")
|
||||||
buf.WriteString(value)
|
buf.WriteString(value)
|
||||||
case 10:
|
case oidSuffixOrganization:
|
||||||
buf.WriteString("/O=")
|
buf.WriteString("/O=")
|
||||||
buf.WriteString(value)
|
buf.WriteString(value)
|
||||||
case 11:
|
case oidSuffixOrganizationalUnit:
|
||||||
buf.WriteString("/OU=")
|
buf.WriteString("/OU=")
|
||||||
buf.WriteString(value)
|
buf.WriteString(value)
|
||||||
}
|
}
|
||||||
|
@ -676,22 +788,25 @@ func opensslFormatDN(subject pkix.Name) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRoot(
|
func NewRoot(
|
||||||
basedir, name, subdir string,
|
basedir, name, subdir string,
|
||||||
id shared.CryptoSystemRootId,
|
id shared.CryptoSystemRootID,
|
||||||
crlDistributionPoints, ocspServers []string,
|
crlDistributionPoints, ocspServers []string,
|
||||||
) *Root {
|
) *Root {
|
||||||
key, err := loadPrivateKey(path.Join(basedir, subdir, "private", "ca.key.pem"))
|
key, err := loadPrivateKey(path.Join(basedir, subdir, "private", "ca.key.pem"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("could not load private key: %v", err)
|
log.Fatalf("could not load private key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cert, err := loadCertificate(path.Join(basedir, subdir, "ca.crt.pem"))
|
cert, err := loadCertificate(path.Join(basedir, subdir, "ca.crt.pem"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("could not load CA certificate: %v", err)
|
log.Fatalf("could not load CA certificate: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
root := &Root{
|
root := &Root{
|
||||||
Name: name,
|
Name: name,
|
||||||
privateKey: key,
|
privateKey: key,
|
||||||
|
@ -709,6 +824,7 @@ func NewRoot(
|
||||||
ocspServers: ocspServers,
|
ocspServers: ocspServers,
|
||||||
}
|
}
|
||||||
root.checkPreconditions()
|
root.checkPreconditions()
|
||||||
|
|
||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,7 +832,7 @@ type AltNameType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
NameTypeDNS AltNameType = "DNS"
|
NameTypeDNS AltNameType = "DNS"
|
||||||
NameTypeXmppJid AltNameType = "otherName:1.3.6.1.5.5.7.8.5;UTF8" // from RFC 3920, 6120
|
NameTypeXMPPJid AltNameType = "otherName:1.3.6.1.5.5.7.8.5;UTF8" // from RFC 3920, 6120
|
||||||
)
|
)
|
||||||
|
|
||||||
type SubjectDnField string
|
type SubjectDnField string
|
||||||
|
@ -744,73 +860,55 @@ func (p *Profile) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Profile) parseSubject(subject []byte) (*pkix.Name, error) {
|
func (p *Profile) parseSubject(subject []byte) (*pkix.Name, error) {
|
||||||
|
|
||||||
parts := strings.Split(string(subject), "/")
|
parts := strings.Split(string(subject), "/")
|
||||||
subjectDN := &pkix.Name{}
|
subjectDN := &pkix.Name{}
|
||||||
|
|
||||||
for _, part := range parts {
|
for _, part := range parts {
|
||||||
if len(strings.TrimSpace(part)) == 0 {
|
if len(strings.TrimSpace(part)) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
handled := false
|
handled := false
|
||||||
item := strings.SplitN(part, "=", 2)
|
item := strings.SplitN(part, "=", 2)
|
||||||
|
|
||||||
for _, f := range p.subjectDNFields {
|
for _, f := range p.subjectDNFields {
|
||||||
if !strings.EqualFold(item[0], string(f)) {
|
if !strings.EqualFold(item[0], string(f)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
value := item[1]
|
value := item[1]
|
||||||
handled = true
|
handled = true
|
||||||
|
|
||||||
switch f {
|
switch f {
|
||||||
case SubjectDnFieldCountryName:
|
case SubjectDnFieldCountryName:
|
||||||
if subjectDN.Country == nil {
|
|
||||||
subjectDN.Country = []string{value}
|
|
||||||
} else {
|
|
||||||
subjectDN.Country = append(subjectDN.Country, value)
|
subjectDN.Country = append(subjectDN.Country, value)
|
||||||
}
|
|
||||||
case SubjectDnFieldStateOrProvinceName:
|
case SubjectDnFieldStateOrProvinceName:
|
||||||
if subjectDN.Province == nil {
|
|
||||||
subjectDN.Province = []string{value}
|
|
||||||
} else {
|
|
||||||
subjectDN.Province = append(subjectDN.Province, value)
|
subjectDN.Province = append(subjectDN.Province, value)
|
||||||
}
|
|
||||||
case SubjectDnFieldLocalityName:
|
case SubjectDnFieldLocalityName:
|
||||||
if subjectDN.Locality == nil {
|
|
||||||
subjectDN.Locality = []string{value}
|
|
||||||
} else {
|
|
||||||
subjectDN.Locality = append(subjectDN.Locality, value)
|
subjectDN.Locality = append(subjectDN.Locality, value)
|
||||||
}
|
|
||||||
case SubjectDnFieldOrganizationName:
|
case SubjectDnFieldOrganizationName:
|
||||||
if subjectDN.Organization == nil {
|
|
||||||
subjectDN.Organization = []string{value}
|
|
||||||
} else {
|
|
||||||
subjectDN.Organization = append(subjectDN.Organization, value)
|
subjectDN.Organization = append(subjectDN.Organization, value)
|
||||||
}
|
|
||||||
case SubjectDnFieldOrganizationalUnitName:
|
case SubjectDnFieldOrganizationalUnitName:
|
||||||
if subjectDN.OrganizationalUnit == nil {
|
|
||||||
subjectDN.OrganizationalUnit = []string{value}
|
|
||||||
} else {
|
|
||||||
subjectDN.OrganizationalUnit = append(subjectDN.OrganizationalUnit, value)
|
subjectDN.OrganizationalUnit = append(subjectDN.OrganizationalUnit, value)
|
||||||
}
|
|
||||||
case SubjectDnFieldCommonName:
|
case SubjectDnFieldCommonName:
|
||||||
subjectDN.CommonName = value
|
subjectDN.CommonName = value
|
||||||
case SubjectDnFieldEmailAddress:
|
case SubjectDnFieldEmailAddress:
|
||||||
emailIA5 := pkix.AttributeTypeAndValue{
|
subjectDN.ExtraNames = append(subjectDN.ExtraNames, pkix.AttributeTypeAndValue{
|
||||||
Type: oidPkcs9EmailAddress,
|
Type: oidPkcs9EmailAddress,
|
||||||
Value: value,
|
Value: value,
|
||||||
}
|
})
|
||||||
if subjectDN.ExtraNames == nil {
|
|
||||||
subjectDN.ExtraNames = []pkix.AttributeTypeAndValue{emailIA5}
|
|
||||||
} else {
|
|
||||||
subjectDN.ExtraNames = append(subjectDN.ExtraNames, emailIA5)
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
log.Warnf("unhandled subject DN type %s", f)
|
log.Warnf("unhandled subject DN type %s", f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !handled {
|
if !handled {
|
||||||
return nil, fmt.Errorf("skipped part %s because it is not supported by profile %s", part, p)
|
return nil, fmt.Errorf("skipped part %s because it is not supported by profile %s", part, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("created subject DN %s", subjectDN)
|
log.Debugf("created subject DN %s", subjectDN)
|
||||||
|
|
||||||
return subjectDN, nil
|
return subjectDN, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -820,19 +918,24 @@ func (p *Profile) parseAltNames(template *x509.Certificate, altNames []byte) err
|
||||||
if len(strings.TrimSpace(part)) == 0 {
|
if len(strings.TrimSpace(part)) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
handled := false
|
handled := false
|
||||||
item := strings.SplitN(part, ":", 3)
|
item := strings.SplitN(part, ":", 3)
|
||||||
|
|
||||||
if item[0] == "otherName" {
|
if item[0] == "otherName" {
|
||||||
item = []string{strings.Join(item[:2], ":"), item[2]}
|
item = []string{strings.Join(item[:2], ":"), item[2]}
|
||||||
} else {
|
} else {
|
||||||
item = []string{item[0], strings.Join(item[1:], ":")}
|
item = []string{item[0], strings.Join(item[1:], ":")}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range p.altNameTypes {
|
for _, f := range p.altNameTypes {
|
||||||
if item[0] != string(f) {
|
if item[0] != string(f) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
value := item[1]
|
value := item[1]
|
||||||
handled = true
|
handled = true
|
||||||
|
|
||||||
switch f {
|
switch f {
|
||||||
case NameTypeDNS:
|
case NameTypeDNS:
|
||||||
if template.DNSNames == nil {
|
if template.DNSNames == nil {
|
||||||
|
@ -840,17 +943,19 @@ func (p *Profile) parseAltNames(template *x509.Certificate, altNames []byte) err
|
||||||
} else {
|
} else {
|
||||||
template.DNSNames = append(template.DNSNames, value)
|
template.DNSNames = append(template.DNSNames, value)
|
||||||
}
|
}
|
||||||
case NameTypeXmppJid:
|
case NameTypeXMPPJid:
|
||||||
// x509.Certificate has no support for otherName alternative names
|
// x509ops.Certificate has no support for otherName alternative names
|
||||||
log.Warnf("skipping %s because it cannot be supported", part)
|
log.Warnf("skipping %s because it cannot be supported", part)
|
||||||
default:
|
default:
|
||||||
log.Warnf("unhandled alternative name type %s", f)
|
log.Warnf("unhandled alternative name type %s", f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !handled {
|
if !handled {
|
||||||
return fmt.Errorf("skipped alternative name %s because it is not supported by profile %s", part, p)
|
return fmt.Errorf("skipped alternative name %s because it is not supported by profile %s", part, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -869,3 +974,14 @@ func NewProfile(
|
||||||
copyEmail: copyEmail,
|
copyEmail: copyEmail,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func stringAsBigInt(data []byte) (*big.Int, error) {
|
||||||
|
dataString := strings.TrimSpace(string(data))
|
||||||
|
|
||||||
|
parseInt, err := strconv.ParseInt(dataString, 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse %s as big int: %w", dataString, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return big.NewInt(parseInt), nil
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package x509_ops
|
package x509ops
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
|
@ -33,6 +33,7 @@ func TestRoot_SignClientCertificateWithCSR(t *testing.T) {
|
||||||
subjectDNFields: []SubjectDnField{SubjectDnFieldCommonName, SubjectDnFieldEmailAddress},
|
subjectDNFields: []SubjectDnField{SubjectDnFieldCommonName, SubjectDnFieldEmailAddress},
|
||||||
copyEmail: true,
|
copyEmail: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
certificate, err := root.SignCertificate(clientProfile, x509.SHA256WithRSA, &SigningRequestParameters{
|
certificate, err := root.SignCertificate(clientProfile, x509.SHA256WithRSA, &SigningRequestParameters{
|
||||||
Request: decodeToBytes(t, testRequest),
|
Request: decodeToBytes(t, testRequest),
|
||||||
Subject: []byte(testClientSubject),
|
Subject: []byte(testClientSubject),
|
||||||
|
@ -42,17 +43,21 @@ func TestRoot_SignClientCertificateWithCSR(t *testing.T) {
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("error signing certificate: %v", err)
|
t.Errorf("error signing certificate: %v", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
certificateDer, _ := pem.Decode(certificate)
|
certificateDer, _ := pem.Decode(certificate)
|
||||||
if certificateDer.Type != "CERTIFICATE" {
|
if certificateDer.Type != pemTypeCertificate {
|
||||||
t.Errorf("invalid PEM type '%s' instead of 'CERTIFICATE'", certificateDer.Type)
|
t.Errorf("invalid PEM type '%s' instead of '%s'", certificateDer.Type, pemTypeCertificate)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = x509.ParseCertificate(certificateDer.Bytes)
|
_, err = x509.ParseCertificate(certificateDer.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not parse generated certificate: %v", err)
|
t.Errorf("could not parse generated certificate: %v", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,17 +95,21 @@ func TestRoot_SignClientCertificateWithSPKAC(t *testing.T) {
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("error signing certificate: %v", err)
|
t.Errorf("error signing certificate: %v", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
certificateDer, _ := pem.Decode(certificate)
|
certificateDer, _ := pem.Decode(certificate)
|
||||||
if certificateDer.Type != "CERTIFICATE" {
|
if certificateDer.Type != pemTypeCertificate {
|
||||||
t.Errorf("invalid PEM type '%s' instead of 'CERTIFICATE'", certificateDer.Type)
|
t.Errorf("invalid PEM type '%s' instead of '%s'", certificateDer.Type, pemTypeCertificate)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = x509.ParseCertificate(certificateDer.Bytes)
|
_, err = x509.ParseCertificate(certificateDer.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not parse generated certificate: %v", err)
|
t.Errorf("could not parse generated certificate: %v", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,9 +143,10 @@ func TestRoot_SignServerCertificateWithCSR(t *testing.T) {
|
||||||
SubjectDnFieldOrganizationalUnitName,
|
SubjectDnFieldOrganizationalUnitName,
|
||||||
SubjectDnFieldCommonName,
|
SubjectDnFieldCommonName,
|
||||||
},
|
},
|
||||||
altNameTypes: []AltNameType{NameTypeDNS, NameTypeXmppJid},
|
altNameTypes: []AltNameType{NameTypeDNS, NameTypeXMPPJid},
|
||||||
copyEmail: false,
|
copyEmail: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
certificate, err := root.SignCertificate(clientProfile, x509.SHA256WithRSA, &SigningRequestParameters{
|
certificate, err := root.SignCertificate(clientProfile, x509.SHA256WithRSA, &SigningRequestParameters{
|
||||||
Request: decodeToBytes(t, testRequest),
|
Request: decodeToBytes(t, testRequest),
|
||||||
Subject: []byte(testServerSubject),
|
Subject: []byte(testServerSubject),
|
||||||
|
@ -146,36 +156,44 @@ func TestRoot_SignServerCertificateWithCSR(t *testing.T) {
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("error signing certificate: %v", err)
|
t.Errorf("error signing certificate: %v", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
certificateDer, _ := pem.Decode(certificate)
|
certificateDer, _ := pem.Decode(certificate)
|
||||||
if certificateDer.Type != "CERTIFICATE" {
|
if certificateDer.Type != "CERTIFICATE" {
|
||||||
t.Errorf("invalid PEM type '%s' instead of 'CERTIFICATE'", certificateDer.Type)
|
t.Errorf("invalid PEM type '%s' instead of 'CERTIFICATE'", certificateDer.Type)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = x509.ParseCertificate(certificateDer.Bytes)
|
_, err = x509.ParseCertificate(certificateDer.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not parse generated certificate: %v", err)
|
t.Errorf("could not parse generated certificate: %v", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadTestKey(t *testing.T) crypto.Signer {
|
func loadTestKey(t *testing.T) crypto.Signer {
|
||||||
testKeyBytes := decodeToBytes(t, testCAKey)
|
testKeyBytes := decodeToBytes(t, testCAKey)
|
||||||
|
|
||||||
key, err := x509.ParsePKCS1PrivateKey(testKeyBytes)
|
key, err := x509.ParsePKCS1PrivateKey(testKeyBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadTestCACertificate(t *testing.T) *x509.Certificate {
|
func loadTestCACertificate(t *testing.T) *x509.Certificate {
|
||||||
testCertBytes := decodeToBytes(t, testCACertificate)
|
testCertBytes := decodeToBytes(t, testCACertificate)
|
||||||
|
|
||||||
cert, err := x509.ParseCertificate(testCertBytes)
|
cert, err := x509.ParseCertificate(testCertBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return cert
|
return cert
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,11 +202,14 @@ func decodeToBytes(t *testing.T, request string) []byte {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return decodeString
|
return decodeString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:lll
|
||||||
const testRequest = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1ZEQ0NBVHdDQVFBd0R6RU5NQXNHQTFVRUF3d0VWR1Z6ZERDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRApnZ0VQQURDQ0FRb0NnZ0VCQU9wQ1JmMVZWQjVwK3RscVFjM25qekYyZVAydG40bGZ2NVlJU3RFMktMSmxJRDRECi9YZTRVUGdodlNuMUN3R1VzeCtQcFBDaTdZVFdQNXRKSWYwbmNLMm02YVZIWUtqcDN2K3NvMnRENkJ4V0lMSFAKS0tQSm5qbnBjU0l1Q3hKUytRU2xyMHh0aEJYYzZ2UzVWRE5Ib285VXJWRUYzSVlTd3VDTklqTWpMR25kYmpCagprNm5TUk5JZWVmeVBaVGI4MHFsVTJFZ3hJMFdFYTA1dm5sQTY5L2tQZGhmTjVRRHBHQ1NxU25GdXo1cGFmRXVIClZMOE1aQXVtVDJySkVkYnorcHRPRjBqMWZSWEQ4b1RKZ0ppQmszbGR6YlBqeFpndW5LSTl3NVcrSWdEWmxsNm8KRzM2TUN6WHNYREdkb25NbCt0K1JIbEdocjN0VDQrKzBobXVYL2pzQ0F3RUFBYUFBTUEwR0NTcUdTSWIzRFFFQgpDd1VBQTRJQkFRRFBlYnhmR3pRaWRud1pxUGdQYUliRmxNM0xsWXpROEJFbFUrYXV5bC90VjhJRC85a0dkekNxClN4N0QyRWVNVC83QmZ6L29XUjRMczVUeFpzdjR0N3RiSEUrSStFdEcwS3FnUDhNTTJPWStGckJBMXVnY3JJa2YKNmVpbXFEVkFtUFBNMHhCNUg3aFdNY1BMVUhzbW1GNlV4ajNsVXphOVQ5OXpxTWppMXlyYlpIc1pkMEM0RFd6RQo1YWtZU1hTTGNuK1F3R25LY1pvV1QwczNWZU5pMHNUK3BTNEVkdk1SbzV6Q3JUMW1SbFlYQkNqU0tpQzZEVjNpCnhyaDI2WWJqMjRKSys5dlNUR3N4RFlpMXUzOG04a1AxRVR2L0lCVnRDSVpKVmJ2eXhWbUpuemV2QnJONHpxdncKV1QvQi9jOGdrK0FQR1BKM3ZaZDUxNVhvM2QzVld4NkwKLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0tCg=="
|
const testRequest = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1ZEQ0NBVHdDQVFBd0R6RU5NQXNHQTFVRUF3d0VWR1Z6ZERDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRApnZ0VQQURDQ0FRb0NnZ0VCQU9wQ1JmMVZWQjVwK3RscVFjM25qekYyZVAydG40bGZ2NVlJU3RFMktMSmxJRDRECi9YZTRVUGdodlNuMUN3R1VzeCtQcFBDaTdZVFdQNXRKSWYwbmNLMm02YVZIWUtqcDN2K3NvMnRENkJ4V0lMSFAKS0tQSm5qbnBjU0l1Q3hKUytRU2xyMHh0aEJYYzZ2UzVWRE5Ib285VXJWRUYzSVlTd3VDTklqTWpMR25kYmpCagprNm5TUk5JZWVmeVBaVGI4MHFsVTJFZ3hJMFdFYTA1dm5sQTY5L2tQZGhmTjVRRHBHQ1NxU25GdXo1cGFmRXVIClZMOE1aQXVtVDJySkVkYnorcHRPRjBqMWZSWEQ4b1RKZ0ppQmszbGR6YlBqeFpndW5LSTl3NVcrSWdEWmxsNm8KRzM2TUN6WHNYREdkb25NbCt0K1JIbEdocjN0VDQrKzBobXVYL2pzQ0F3RUFBYUFBTUEwR0NTcUdTSWIzRFFFQgpDd1VBQTRJQkFRRFBlYnhmR3pRaWRud1pxUGdQYUliRmxNM0xsWXpROEJFbFUrYXV5bC90VjhJRC85a0dkekNxClN4N0QyRWVNVC83QmZ6L29XUjRMczVUeFpzdjR0N3RiSEUrSStFdEcwS3FnUDhNTTJPWStGckJBMXVnY3JJa2YKNmVpbXFEVkFtUFBNMHhCNUg3aFdNY1BMVUhzbW1GNlV4ajNsVXphOVQ5OXpxTWppMXlyYlpIc1pkMEM0RFd6RQo1YWtZU1hTTGNuK1F3R25LY1pvV1QwczNWZU5pMHNUK3BTNEVkdk1SbzV6Q3JUMW1SbFlYQkNqU0tpQzZEVjNpCnhyaDI2WWJqMjRKSys5dlNUR3N4RFlpMXUzOG04a1AxRVR2L0lCVnRDSVpKVmJ2eXhWbUpuemV2QnJONHpxdncKV1QvQi9jOGdrK0FQR1BKM3ZaZDUxNVhvM2QzVld4NkwKLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0tCg=="
|
||||||
|
|
||||||
|
// nolint:lll
|
||||||
const testSpkac = "U1BLQUM9TUlJQ1FEQ0NBU2d3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRRHFRa1g5VlZRZWFmclpha0hONTQ4eGRuajlyWitKWDcrV0NFclJOaWl5WlNBK0EvMTN1RkQ0SWIwcDlRc0JsTE1majZUd291MkUxaitiU1NIOUozQ3RwdW1sUjJDbzZkNy9yS05yUStnY1ZpQ3h6eWlqeVo0NTZYRWlMZ3NTVXZrRXBhOU1iWVFWM09yMHVWUXpSNktQVksxUkJkeUdFc0xnalNJekl5eHAzVzR3WTVPcDBrVFNIbm44ajJVMi9OS3BWTmhJTVNORmhHdE9iNTVRT3ZmNUQzWVh6ZVVBNlJna3FrcHhicythV254TGgxUy9ER1FMcGs5cXlSSFc4L3FiVGhkSTlYMFZ3L0tFeVlDWWdaTjVYYzJ6NDhXWUxweWlQY09WdmlJQTJaWmVxQnQrakFzMTdGd3huYUp6SmZyZmtSNVJvYTk3VStQdnRJWnJsLzQ3QWdNQkFBRVdBREFOQmdrcWhraUc5dzBCQVFRRkFBT0NBUUVBaDdqWmxaYXpPOXdHRkl3Mll1SVN5WjVYb2JjU0pSS2dMbG52UDh5cUkyVkxTdVBmdkNXOGJxKzNMWnNWcFZ6U0dhbDgwUTk5empOVy9lRm0xTElMRXZNZ0FCeEFtdk9UNlNKZURyajc4WkQyL0haazdmdHVLWU1VbkZTOWhzUWlkWmoveUttbDd4Nm9hQnlpalg5UVU0eTZYcCs3NzlhREFSenZOWTR5RGlRZmNuaFBVN1dablJyTUJZOHBSTVJVVjNKc2MvZXBQclZjOVhoaVE0KzBRNHFVQW1VNlVHNWNQeXFROVJJK2ZIL3VMbm8vZkNHUGZFVjM1NDV3WG5NeXNubC9HYlNLa0FHdVFmcm5ZT2dReFV4dUx1aHBoazVIaGtlMzNUUy9FYVlGc2JTZEhzRktoaCt4Uks3NW9wWExJTkpzNTFpYVdySUFTVmZvRTI0a3FRPT0K"
|
const testSpkac = "U1BLQUM9TUlJQ1FEQ0NBU2d3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRRHFRa1g5VlZRZWFmclpha0hONTQ4eGRuajlyWitKWDcrV0NFclJOaWl5WlNBK0EvMTN1RkQ0SWIwcDlRc0JsTE1majZUd291MkUxaitiU1NIOUozQ3RwdW1sUjJDbzZkNy9yS05yUStnY1ZpQ3h6eWlqeVo0NTZYRWlMZ3NTVXZrRXBhOU1iWVFWM09yMHVWUXpSNktQVksxUkJkeUdFc0xnalNJekl5eHAzVzR3WTVPcDBrVFNIbm44ajJVMi9OS3BWTmhJTVNORmhHdE9iNTVRT3ZmNUQzWVh6ZVVBNlJna3FrcHhicythV254TGgxUy9ER1FMcGs5cXlSSFc4L3FiVGhkSTlYMFZ3L0tFeVlDWWdaTjVYYzJ6NDhXWUxweWlQY09WdmlJQTJaWmVxQnQrakFzMTdGd3huYUp6SmZyZmtSNVJvYTk3VStQdnRJWnJsLzQ3QWdNQkFBRVdBREFOQmdrcWhraUc5dzBCQVFRRkFBT0NBUUVBaDdqWmxaYXpPOXdHRkl3Mll1SVN5WjVYb2JjU0pSS2dMbG52UDh5cUkyVkxTdVBmdkNXOGJxKzNMWnNWcFZ6U0dhbDgwUTk5empOVy9lRm0xTElMRXZNZ0FCeEFtdk9UNlNKZURyajc4WkQyL0haazdmdHVLWU1VbkZTOWhzUWlkWmoveUttbDd4Nm9hQnlpalg5UVU0eTZYcCs3NzlhREFSenZOWTR5RGlRZmNuaFBVN1dablJyTUJZOHBSTVJVVjNKc2MvZXBQclZjOVhoaVE0KzBRNHFVQW1VNlVHNWNQeXFROVJJK2ZIL3VMbm8vZkNHUGZFVjM1NDV3WG5NeXNubC9HYlNLa0FHdVFmcm5ZT2dReFV4dUx1aHBoazVIaGtlMzNUUy9FYVlGc2JTZEhzRktoaCt4Uks3NW9wWExJTkpzNTFpYVdySUFTVmZvRTI0a3FRPT0K"
|
||||||
|
|
||||||
const testClientSubject = "/CN=Test/emailAddress=test@example.org"
|
const testClientSubject = "/CN=Test/emailAddress=test@example.org"
|
||||||
|
@ -199,6 +220,8 @@ const testServerSubject = "/CN=www.example.org"
|
||||||
|
|
||||||
const testServerSan = "DNS:www.example.org,otherName:1.3.6.1.5.5.7.8.5;UTF8:www.example.org"
|
const testServerSan = "DNS:www.example.org,otherName:1.3.6.1.5.5.7.8.5;UTF8:www.example.org"
|
||||||
|
|
||||||
|
// nolint:lll
|
||||||
const testCAKey = "MIIG5AIBAAKCAYEAqMWP2Ec/DxJ5n5bCv0e6oTBoGvsplq1qHVtwPDEL/bwlhUbTigGPQGoUK+Y3eS2T5FY/qpdfqjUBi/FnPiKNwEnmkYuSakSSS6GyrgsP876z1xXth50CIkUnAPR1YJ0bYmhUpEitRJgoWa5my3bS+LuNt1gVHD+zyCOlbfNJZTILnQHFLtzi/wPivlTWpUDJzHWvvo+Ki4e29qWRMaAatiXLUq/wW06fsRSa9plkhNv7jlg9hq8Y2SEie0mRvuyFgIKvkBmcT3X5yPhCWZPomsqQnEJXKnxno0SkrM+XoWWBeusYPsZkfXknGwy/wvoMbVT5MfqyMYY1CTw8/zaSDoC/sj8XmAL44t+EsZ+JEUYgSVW6Y3L2KieuqCibg4B+G8qI4AQm2cjXanjX1kWTUCCtGO6ylxRKNq0zCWhflE2i2s/+4v+RuHQ1laYnfl3vIQZHz0/gtQIlR2AqXc2ODRoTO8d80dXZjwImnrjHQ/yHx4LErHMprNjQb0BprtCDAgMBAAECggGAEF7hfhQjHL4pB/7isxUtGDeO0ZctSI1XrrNQ5rXHOPyIEy50lH1kPNZNUJjLJrjyEIMBN/Xo9KShmsZ2wkMtxsokUFfegupV2no7z8AI8xa7cRCScsYbD+HvT5tmy1FR97CxDSJzlCTCPTi6hd/nxPLEY1Vq7suLD83NXSXtJ6C8GaWzT8FjT2M8GkQ2cd8f8/IycuSPhstKRxB2Tf7+uE5gM4wXX3P374BVK7hjVLPV6c/LYAYZ/e3F33maZo+glyRP0DIWUtVQHhn7ZxlhatLYPrzwoM9MBFVjX4KHjRBWpZ/eSRKmDske0KnQ8nKPo2MsXxm6aKRNr4XJRC0FXqvz1CEa22ANOtCyfNmHHH6PC7R9rCCt8TZFAPDyyVq31KJe89cwdPngPBOIZdnW9U5pmG0aQrwU9ubafX5Yf+uDP0N9rEPEw5sU0QZHMai5751jQFIpej/6IA3mv0rscxP5Bjc8gGJynhj4BvpHWHZzdRQZquQG1CPKDm5yItuBAoHBANhF+ukAUkIvWMuD9WXSGcUuDsfPOSvx5Fx2riQYhbDFhb2W5zZ75Pi4E2OnQ8RDxCbUJ/iubLwm69LixW5e2+hvR8+AFV3bBgXQUl4uJHKCChz94JZHNgaUae1jjqWNxINLWNAIGUzR9ABslWRiE15InjOjMLf0is0IqryPUX1JtHLM9HnDtR5RbuZUhEUxp3a/msJJeM1sVbOMELolmC0O6ChhMM2mj7mSkexSE7XZwJLn+pIP5GZBhdi0Jz++wQKBwQDHxd55McC7gEIOPjy4SUHhE/JnJd0MRr6R2kHiZs7yMy8WBjDp+Ez2JiHHj00HY9hTRswO45ZtfaMFzfAjLJ7Dja7Pvbh48QkZJV+bul1pLdsLnzHtxqDaZSZluGBBUMoh+PE7WauGgflxtWrH0QX1kv+E0Z70F1fgsJkJ7L9j+M9TkKJxOtqS0BVo13Ko2LHN+6hFOeE5J7ItvdapWPXBGUySw9ELmLd38br0wTdzpWD6OupcM8M8Qb+vncEc5EMCgcEA1Vby07VFb5RU+y0IfZBra16rpd58fyT2J1/LGEA4YM/3xbV+DvjYPaEXP05YQtq2O7c8Vst454FdT4HzT5SzSO284KtwaE0N+94r4kuSGIK+hyrIyHUmjgcJFusGY7kdCIbi7ROQIX9aOrDiDUvR30ezBy0Leer4oJjUE30s3XI/Vp9m6lZr66RYyUzFzZvVngYUG2NujvU29Q5N0dIT8x6pVGvLQJH1ZRF4cK3mU5Shqki7nCmhHF22MrZDoVYBAoHBALCmIC5sty9Vn5N2pzyR0sZTXBKnoYo8eEECjSXEoRP7/JPuD4ykenFikJYk+gkh2eTxgnlb9+WDpgb47nI7/3uOKlkaOyf+g3wP1zYeGoFqAfqJ352Q+SWFMenamorHBKX7ulwv04OSJN/OesiL5UgcnwN0VKkkhxlxLzJefXLKTZJoH6weTa5qf7QAZyw0yS0KbeYg4y4mEuFtr4Z52n3QgCx7KLunY/yU7SuGOyFwyIscU6YKQ4Zh4T1KMrv4fwKBwExZH2XIvvu7setxg6IkNMnNJdeJ6mefk3kxdZX+ZprO3cyh60bv0lSjqrKADQuy2MknQKjx0NvI4vbzmhUUb18Koy66oh4r7M5iSKofWs3rybfeGjF4StETSW7fS1nLGlicYqIbX6TT4Hhg91RwT33vrEvvlBQFowV8cR5OmGq6aW6H6bh3UkzcxV2HI/QvwW2mvRvDQycnjfGjuYbVwi6tn2O2wet0Dka7y/AZfp9OBLJRBZJNoIViTn4Lx9FHlQ=="
|
const testCAKey = "MIIG5AIBAAKCAYEAqMWP2Ec/DxJ5n5bCv0e6oTBoGvsplq1qHVtwPDEL/bwlhUbTigGPQGoUK+Y3eS2T5FY/qpdfqjUBi/FnPiKNwEnmkYuSakSSS6GyrgsP876z1xXth50CIkUnAPR1YJ0bYmhUpEitRJgoWa5my3bS+LuNt1gVHD+zyCOlbfNJZTILnQHFLtzi/wPivlTWpUDJzHWvvo+Ki4e29qWRMaAatiXLUq/wW06fsRSa9plkhNv7jlg9hq8Y2SEie0mRvuyFgIKvkBmcT3X5yPhCWZPomsqQnEJXKnxno0SkrM+XoWWBeusYPsZkfXknGwy/wvoMbVT5MfqyMYY1CTw8/zaSDoC/sj8XmAL44t+EsZ+JEUYgSVW6Y3L2KieuqCibg4B+G8qI4AQm2cjXanjX1kWTUCCtGO6ylxRKNq0zCWhflE2i2s/+4v+RuHQ1laYnfl3vIQZHz0/gtQIlR2AqXc2ODRoTO8d80dXZjwImnrjHQ/yHx4LErHMprNjQb0BprtCDAgMBAAECggGAEF7hfhQjHL4pB/7isxUtGDeO0ZctSI1XrrNQ5rXHOPyIEy50lH1kPNZNUJjLJrjyEIMBN/Xo9KShmsZ2wkMtxsokUFfegupV2no7z8AI8xa7cRCScsYbD+HvT5tmy1FR97CxDSJzlCTCPTi6hd/nxPLEY1Vq7suLD83NXSXtJ6C8GaWzT8FjT2M8GkQ2cd8f8/IycuSPhstKRxB2Tf7+uE5gM4wXX3P374BVK7hjVLPV6c/LYAYZ/e3F33maZo+glyRP0DIWUtVQHhn7ZxlhatLYPrzwoM9MBFVjX4KHjRBWpZ/eSRKmDske0KnQ8nKPo2MsXxm6aKRNr4XJRC0FXqvz1CEa22ANOtCyfNmHHH6PC7R9rCCt8TZFAPDyyVq31KJe89cwdPngPBOIZdnW9U5pmG0aQrwU9ubafX5Yf+uDP0N9rEPEw5sU0QZHMai5751jQFIpej/6IA3mv0rscxP5Bjc8gGJynhj4BvpHWHZzdRQZquQG1CPKDm5yItuBAoHBANhF+ukAUkIvWMuD9WXSGcUuDsfPOSvx5Fx2riQYhbDFhb2W5zZ75Pi4E2OnQ8RDxCbUJ/iubLwm69LixW5e2+hvR8+AFV3bBgXQUl4uJHKCChz94JZHNgaUae1jjqWNxINLWNAIGUzR9ABslWRiE15InjOjMLf0is0IqryPUX1JtHLM9HnDtR5RbuZUhEUxp3a/msJJeM1sVbOMELolmC0O6ChhMM2mj7mSkexSE7XZwJLn+pIP5GZBhdi0Jz++wQKBwQDHxd55McC7gEIOPjy4SUHhE/JnJd0MRr6R2kHiZs7yMy8WBjDp+Ez2JiHHj00HY9hTRswO45ZtfaMFzfAjLJ7Dja7Pvbh48QkZJV+bul1pLdsLnzHtxqDaZSZluGBBUMoh+PE7WauGgflxtWrH0QX1kv+E0Z70F1fgsJkJ7L9j+M9TkKJxOtqS0BVo13Ko2LHN+6hFOeE5J7ItvdapWPXBGUySw9ELmLd38br0wTdzpWD6OupcM8M8Qb+vncEc5EMCgcEA1Vby07VFb5RU+y0IfZBra16rpd58fyT2J1/LGEA4YM/3xbV+DvjYPaEXP05YQtq2O7c8Vst454FdT4HzT5SzSO284KtwaE0N+94r4kuSGIK+hyrIyHUmjgcJFusGY7kdCIbi7ROQIX9aOrDiDUvR30ezBy0Leer4oJjUE30s3XI/Vp9m6lZr66RYyUzFzZvVngYUG2NujvU29Q5N0dIT8x6pVGvLQJH1ZRF4cK3mU5Shqki7nCmhHF22MrZDoVYBAoHBALCmIC5sty9Vn5N2pzyR0sZTXBKnoYo8eEECjSXEoRP7/JPuD4ykenFikJYk+gkh2eTxgnlb9+WDpgb47nI7/3uOKlkaOyf+g3wP1zYeGoFqAfqJ352Q+SWFMenamorHBKX7ulwv04OSJN/OesiL5UgcnwN0VKkkhxlxLzJefXLKTZJoH6weTa5qf7QAZyw0yS0KbeYg4y4mEuFtr4Z52n3QgCx7KLunY/yU7SuGOyFwyIscU6YKQ4Zh4T1KMrv4fwKBwExZH2XIvvu7setxg6IkNMnNJdeJ6mefk3kxdZX+ZprO3cyh60bv0lSjqrKADQuy2MknQKjx0NvI4vbzmhUUb18Koy66oh4r7M5iSKofWs3rybfeGjF4StETSW7fS1nLGlicYqIbX6TT4Hhg91RwT33vrEvvlBQFowV8cR5OmGq6aW6H6bh3UkzcxV2HI/QvwW2mvRvDQycnjfGjuYbVwi6tn2O2wet0Dka7y/AZfp9OBLJRBZJNoIViTn4Lx9FHlQ=="
|
||||||
|
|
||||||
|
// nolint:lll
|
||||||
const testCACertificate = "MIIFmDCCBACgAwIBAgIUOlITUGXFrKZesL4LlawzVxLTXFYwDQYJKoZIhvcNAQELBQAwNzELMAkGA1UEBhMCQVUxFDASBgNVBAoMC0NBY2VydCBJbmMuMRIwEAYDVQQDDAlUZXN0IFJvb3QwHhcNMjEwMTA4MTMwNDM5WhcNMjYwMTA3MTMwNDM5WjA9MQswCQYDVQQGEwJBVTEUMBIGA1UECgwLQ0FjZXJ0IEluYy4xGDAWBgNVBAMMD0NsYXNzIDMgVGVzdCBDQTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAKjFj9hHPw8SeZ+Wwr9HuqEwaBr7KZatah1bcDwxC/28JYVG04oBj0BqFCvmN3ktk+RWP6qXX6o1AYvxZz4ijcBJ5pGLkmpEkkuhsq4LD/O+s9cV7YedAiJFJwD0dWCdG2JoVKRIrUSYKFmuZst20vi7jbdYFRw/s8gjpW3zSWUyC50BxS7c4v8D4r5U1qVAycx1r76PiouHtvalkTGgGrYly1Kv8FtOn7EUmvaZZITb+45YPYavGNkhIntJkb7shYCCr5AZnE91+cj4QlmT6JrKkJxCVyp8Z6NEpKzPl6FlgXrrGD7GZH15JxsMv8L6DG1U+TH6sjGGNQk8PP82kg6Av7I/F5gC+OLfhLGfiRFGIElVumNy9ionrqgom4OAfhvKiOAEJtnI12p419ZFk1AgrRjuspcUSjatMwloX5RNotrP/uL/kbh0NZWmJ35d7yEGR89P4LUCJUdgKl3Njg0aEzvHfNHV2Y8CJp64x0P8h8eCxKxzKazY0G9Aaa7QgwIDAQABo4IBlDCCAZAwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBRI3ALoQ7dcRqu0gHtxaLVAv2DcOzAfBgNVHSMEGDAWgBTrwahlUMEoV1OavhMbkLcSar8PIzB3BggrBgEFBQcBAQRrMGkwNwYIKwYBBQUHMAKGK2h0dHA6Ly90ZXN0LmNhY2VydC5sb2NhbGhvc3QvY2Evcm9vdC9jYS5jcnQwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLnRlc3QuY2FjZXJ0LmxvY2FsaG9zdC8wPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC50ZXN0LmNhY2VydC5sb2NhbGhvc3QvY2xhc3MzLmNybDBUBgNVHSAETTBLMEkGCCsGAQUFBwIBMD0wOwYIKwYBBQUHAgEWL2h0dHA6Ly90ZXN0LmNhY2VydC5sb2NhbGhvc3QvY2EvY2xhc3MzL2Nwcy5odG1sMA0GCSqGSIb3DQEBCwUAA4IBgQAcqK68GOxTfM9zSRbHWHchsbiyKcbxPo42se9dm/nLHT/N2XEW9Ycj5dZD8+XgoW8dVPS3uVZGj57Pr8ix3OXhMKGqcdO2QRAQaoyjw7t9dCkaJ8b7h39sY/5pFSSIdYAyyb9uPgJ1FPLueOqm3bZHVFcbiiA8/miiwGWPVEfK7zdEmFKMAkY2wYtWBeovKNVnCbuQ1Pd8CxvkCs5R9KnMfbU7bgJK8zkhlHwdtalmg2IS4yMuvYeL9S3QwL7fYcCjjTLCKwkj3frsnkRC5pGPHQ6/iVVbdsqAI70A1Uqcl15Jcpzg0Nc2EABjhbWO7gLpHpzMI5Alt+Tr+oWhe2M7wnBhuojgwASA10CnXT27GYXziIzr8d3P+T0PVLD2WcvQeEUJoQySw6W8CIkaZEZG6YBWjrAkGcO6JB+YJ5UiJOCHA6W4pmwNkGR2oh6JMQCUikaFVywb1HMIGOINOBHymj4KkuywC2w6SXMD4OqJcsCmHSNcqjFvcT/22kYCtDE="
|
const testCACertificate = "MIIFmDCCBACgAwIBAgIUOlITUGXFrKZesL4LlawzVxLTXFYwDQYJKoZIhvcNAQELBQAwNzELMAkGA1UEBhMCQVUxFDASBgNVBAoMC0NBY2VydCBJbmMuMRIwEAYDVQQDDAlUZXN0IFJvb3QwHhcNMjEwMTA4MTMwNDM5WhcNMjYwMTA3MTMwNDM5WjA9MQswCQYDVQQGEwJBVTEUMBIGA1UECgwLQ0FjZXJ0IEluYy4xGDAWBgNVBAMMD0NsYXNzIDMgVGVzdCBDQTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAKjFj9hHPw8SeZ+Wwr9HuqEwaBr7KZatah1bcDwxC/28JYVG04oBj0BqFCvmN3ktk+RWP6qXX6o1AYvxZz4ijcBJ5pGLkmpEkkuhsq4LD/O+s9cV7YedAiJFJwD0dWCdG2JoVKRIrUSYKFmuZst20vi7jbdYFRw/s8gjpW3zSWUyC50BxS7c4v8D4r5U1qVAycx1r76PiouHtvalkTGgGrYly1Kv8FtOn7EUmvaZZITb+45YPYavGNkhIntJkb7shYCCr5AZnE91+cj4QlmT6JrKkJxCVyp8Z6NEpKzPl6FlgXrrGD7GZH15JxsMv8L6DG1U+TH6sjGGNQk8PP82kg6Av7I/F5gC+OLfhLGfiRFGIElVumNy9ionrqgom4OAfhvKiOAEJtnI12p419ZFk1AgrRjuspcUSjatMwloX5RNotrP/uL/kbh0NZWmJ35d7yEGR89P4LUCJUdgKl3Njg0aEzvHfNHV2Y8CJp64x0P8h8eCxKxzKazY0G9Aaa7QgwIDAQABo4IBlDCCAZAwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBRI3ALoQ7dcRqu0gHtxaLVAv2DcOzAfBgNVHSMEGDAWgBTrwahlUMEoV1OavhMbkLcSar8PIzB3BggrBgEFBQcBAQRrMGkwNwYIKwYBBQUHMAKGK2h0dHA6Ly90ZXN0LmNhY2VydC5sb2NhbGhvc3QvY2Evcm9vdC9jYS5jcnQwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLnRlc3QuY2FjZXJ0LmxvY2FsaG9zdC8wPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC50ZXN0LmNhY2VydC5sb2NhbGhvc3QvY2xhc3MzLmNybDBUBgNVHSAETTBLMEkGCCsGAQUFBwIBMD0wOwYIKwYBBQUHAgEWL2h0dHA6Ly90ZXN0LmNhY2VydC5sb2NhbGhvc3QvY2EvY2xhc3MzL2Nwcy5odG1sMA0GCSqGSIb3DQEBCwUAA4IBgQAcqK68GOxTfM9zSRbHWHchsbiyKcbxPo42se9dm/nLHT/N2XEW9Ycj5dZD8+XgoW8dVPS3uVZGj57Pr8ix3OXhMKGqcdO2QRAQaoyjw7t9dCkaJ8b7h39sY/5pFSSIdYAyyb9uPgJ1FPLueOqm3bZHVFcbiiA8/miiwGWPVEfK7zdEmFKMAkY2wYtWBeovKNVnCbuQ1Pd8CxvkCs5R9KnMfbU7bgJK8zkhlHwdtalmg2IS4yMuvYeL9S3QwL7fYcCjjTLCKwkj3frsnkRC5pGPHQ6/iVVbdsqAI70A1Uqcl15Jcpzg0Nc2EABjhbWO7gLpHpzMI5Alt+Tr+oWhe2M7wnBhuojgwASA10CnXT27GYXziIzr8d3P+T0PVLD2WcvQeEUJoQySw6W8CIkaZEZG6YBWjrAkGcO6JB+YJ5UiJOCHA6W4pmwNkGR2oh6JMQCUikaFVywb1HMIGOINOBHymj4KkuywC2w6SXMD4OqJcsCmHSNcqjFvcT/22kYCtDE="
|
Loading…
Reference in a new issue