Implement CA information command

This commit defines command codes for planned commands and response codes for
their corresponding responses.

The health response from the HSM access component has been reduced to avoid
unnecessary data transmissions.

A new CA information command has been implemented. This command can be used
to retrieve the CA certificate and profile information for a given CA name.

The client simulator has been updated to retrieve CA information for all
CAs when the list of CAs changes.
main
Jan Dittberner 2 years ago
parent 6f8ac9818c
commit afe7d23c9b

@ -22,9 +22,11 @@ package main
import ( import (
"context" "context"
"crypto/rand" "crypto/rand"
"crypto/x509"
"fmt" "fmt"
"io" "io"
"os" "os"
"sort"
"time" "time"
"github.com/shamaton/msgpackgen/msgpack" "github.com/shamaton/msgpackgen/msgpack"
@ -167,7 +169,9 @@ func (c *clientSimulator) Run(ctx context.Context) error {
} }
type ClientHandler struct { type ClientHandler struct {
logger *logrus.Logger logger *logrus.Logger
commands chan *protocol.Command
caList []string
} }
func (c *ClientHandler) Send(ctx context.Context, command *protocol.Command, out chan []byte) error { func (c *ClientHandler) Send(ctx context.Context, command *protocol.Command, out chan []byte) error {
@ -244,6 +248,29 @@ func (c *ClientHandler) ResponseData(ctx context.Context, in chan []byte, respon
return fmt.Errorf("could not unmarshal health response data: %w", err) return fmt.Errorf("could not unmarshal health response data: %w", err)
} }
c.updateCAs(ctx, c.commands, resp.Info)
response.Response = &resp
case messages.RespCAInfo:
var resp messages.CAInfoResponse
if err := msgpack.Unmarshal(frame, &resp); err != nil {
return fmt.Errorf("could not unmarshal CA info response data: %w", err)
}
certificate, err := x509.ParseCertificate(resp.Certificate)
if err != nil {
return fmt.Errorf("could not parse certificate data: %w", err)
}
c.logger.Infof(
"certificate for %s: subject=%s, issuer=%s, serial=0x%x, valid from %s to %s",
resp.Name,
certificate.Subject,
certificate.Issuer,
certificate.SerialNumber,
certificate.NotBefore,
certificate.NotAfter)
response.Response = &resp response.Response = &resp
case messages.RespFetchCRL: case messages.RespFetchCRL:
var resp messages.FetchCRLResponse var resp messages.FetchCRLResponse
@ -269,8 +296,40 @@ func (c *ClientHandler) HandleResponse(_ context.Context, response *protocol.Res
return nil return nil
} }
func newClientHandler(logger *logrus.Logger) *ClientHandler { func (c *ClientHandler) updateCAs(ctx context.Context, out chan *protocol.Command, info []*messages.HealthInfo) {
return &ClientHandler{logger: logger} caList := make([]string, 0)
for _, i := range info {
if i.Source == "HSM" {
c.logger.Debugf("info from HSM: %s", i)
for caName := range i.MoreInfo {
caList = append(caList, caName)
}
}
}
sort.Strings(caList)
if len(caList) != len(c.caList) {
c.caList = caList
for _, ca := range c.caList {
select {
case <-ctx.Done():
return
case out <- &protocol.Command{
Announce: messages.BuildCommandAnnounce(messages.CmdCAInfo),
Command: &messages.CAInfoCommand{Name: ca},
}:
continue
}
}
}
}
func newClientHandler(logger *logrus.Logger, commands chan *protocol.Command) *ClientHandler {
return &ClientHandler{logger: logger, commands: commands}
} }
func main() { func main() {
@ -285,16 +344,20 @@ func main() {
logger.WithError(err).Fatal("could not create COBS framer") logger.WithError(err).Fatal("could not create COBS framer")
} }
commandBufferSize := 50
commandCh := make(chan *protocol.Command, commandBufferSize)
sim := &clientSimulator{ sim := &clientSimulator{
commandGenerator: &TestCommandGenerator{ commandGenerator: &TestCommandGenerator{
logger: logger, logger: logger,
commands: make(chan *protocol.Command), commands: commandCh,
}, },
logger: logger, logger: logger,
framesIn: make(chan []byte), framesIn: make(chan []byte),
framesOut: make(chan []byte), framesOut: make(chan []byte),
framer: cobsFramer, framer: cobsFramer,
clientHandler: newClientHandler(logger), clientHandler: newClientHandler(logger, commandCh),
} }
err = sim.Run(context.Background()) err = sim.Run(context.Background())

@ -105,6 +105,7 @@ func main() {
logger, logger,
handler.RegisterHealthHandler(healthHandler), handler.RegisterHealthHandler(healthHandler),
handler.RegisterFetchCRLHandler(fetchCRLHandler), handler.RegisterFetchCRLHandler(fetchCRLHandler),
handler.RegisterCAInfoHandler(access),
) )
if err != nil { if err != nil {
logger.WithError(err).Fatal("could not setup protocol handler") logger.WithError(err).Fatal("could not setup protocol handler")

@ -0,0 +1,29 @@
/*
Copyright 2022 CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cainfo
import "git.cacert.org/cacert-gosigner/pkg/messages"
type Result struct {
Certificate []byte
Profiles []messages.CAProfile
}
type Handler interface {
GetCAInfo(string) (*Result, error)
}

@ -27,11 +27,11 @@ import (
"github.com/shamaton/msgpackgen/msgpack" "github.com/shamaton/msgpackgen/msgpack"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"git.cacert.org/cacert-gosigner/pkg/protocol" "git.cacert.org/cacert-gosigner/internal/cainfo"
"git.cacert.org/cacert-gosigner/internal/health" "git.cacert.org/cacert-gosigner/internal/health"
"git.cacert.org/cacert-gosigner/internal/x509/revoking" "git.cacert.org/cacert-gosigner/internal/x509/revoking"
"git.cacert.org/cacert-gosigner/pkg/messages" "git.cacert.org/cacert-gosigner/pkg/messages"
"git.cacert.org/cacert-gosigner/pkg/protocol"
) )
const readCommandTimeOut = 5 * time.Second const readCommandTimeOut = 5 * time.Second
@ -40,9 +40,10 @@ var errReadCommandTimeout = errors.New("read command timeout expired")
// MsgPackHandler is a ServerHandler implementation for the msgpack serialization format. // MsgPackHandler is a ServerHandler implementation for the msgpack serialization format.
type MsgPackHandler struct { type MsgPackHandler struct {
logger *logrus.Logger logger *logrus.Logger
healthHandler *health.Handler healthHandler *health.Handler
fetchCRLHandler *revoking.FetchCRLHandler certificateAuthorityInfoHandler cainfo.Handler
fetchCRLHandler *revoking.FetchCRLHandler
} }
func (m *MsgPackHandler) CommandAnnounce(ctx context.Context, frames chan []byte) (*protocol.Command, error) { func (m *MsgPackHandler) CommandAnnounce(ctx context.Context, frames chan []byte) (*protocol.Command, error) {
@ -149,6 +150,18 @@ func (m *MsgPackHandler) parseHealthCommand(frame []byte) (*messages.HealthComma
return &command, nil return &command, nil
} }
func (m *MsgPackHandler) parseCAInfoCommand(frame []byte) (*messages.CAInfoCommand, error) {
var command messages.CAInfoCommand
if err := msgpack.Unmarshal(frame, &command); err != nil {
m.logger.WithError(err).Error("unmarshal failed")
return nil, errors.New("could not unmarshal CA info command")
}
return &command, nil
}
func (m *MsgPackHandler) parseFetchCRLCommand(frame []byte) (*messages.FetchCRLCommand, error) { func (m *MsgPackHandler) parseFetchCRLCommand(frame []byte) (*messages.FetchCRLCommand, error) {
var command messages.FetchCRLCommand var command messages.FetchCRLCommand
@ -175,6 +188,13 @@ func (m *MsgPackHandler) handleCommand(command *protocol.Command) (*protocol.Res
} }
responseCode, responseData = messages.RespHealth, response responseCode, responseData = messages.RespHealth, response
case *messages.CAInfoCommand:
response, err := m.handleCAInfoCommand(cmd)
if err != nil {
return nil, err
}
responseCode, responseData = messages.RespCAInfo, response
case *messages.FetchCRLCommand: case *messages.FetchCRLCommand:
response, err := m.handleFetchCRLCommand(cmd) response, err := m.handleFetchCRLCommand(cmd)
if err != nil { if err != nil {
@ -208,6 +228,13 @@ func (m *MsgPackHandler) parseCommand(frame []byte, command *protocol.Command) e
} }
command.Command = healthCommand command.Command = healthCommand
case messages.CmdCAInfo:
caInfoCommand, err := m.parseCAInfoCommand(frame)
if err != nil {
return err
}
command.Command = caInfoCommand
case messages.CmdFetchCRL: case messages.CmdFetchCRL:
fetchCRLCommand, err := m.parseFetchCRLCommand(frame) fetchCRLCommand, err := m.parseFetchCRLCommand(frame)
if err != nil { if err != nil {
@ -244,6 +271,22 @@ func (m *MsgPackHandler) handleHealthCommand() (*messages.HealthResponse, error)
return response, nil return response, nil
} }
func (m *MsgPackHandler) handleCAInfoCommand(command *messages.CAInfoCommand) (*messages.CAInfoResponse, error) {
res, err := m.certificateAuthorityInfoHandler.GetCAInfo(command.Name)
if err != nil {
return nil, fmt.Errorf("could not get CA information")
}
response := &messages.CAInfoResponse{
Name: command.Name,
Certificate: res.Certificate,
Signing: res.Certificate != nil && len(res.Profiles) > 0,
Profiles: res.Profiles,
}
return response, nil
}
func (m *MsgPackHandler) handleFetchCRLCommand(command *messages.FetchCRLCommand) (*messages.FetchCRLResponse, error) { func (m *MsgPackHandler) handleFetchCRLCommand(command *messages.FetchCRLCommand) (*messages.FetchCRLResponse, error) {
var crlNumber *big.Int var crlNumber *big.Int
@ -299,3 +342,9 @@ func RegisterFetchCRLHandler(fetchCRLHandler *revoking.FetchCRLHandler) func(han
h.fetchCRLHandler = fetchCRLHandler h.fetchCRLHandler = fetchCRLHandler
} }
} }
func RegisterCAInfoHandler(caInfoHandler cainfo.Handler) func(handler *MsgPackHandler) {
return func(h *MsgPackHandler) {
h.certificateAuthorityInfoHandler = caInfoHandler
}
}

@ -36,10 +36,10 @@ import (
"github.com/ThalesIgnite/crypto11" "github.com/ThalesIgnite/crypto11"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"git.cacert.org/cacert-gosigner/pkg/messages" "git.cacert.org/cacert-gosigner/internal/cainfo"
"git.cacert.org/cacert-gosigner/internal/config" "git.cacert.org/cacert-gosigner/internal/config"
"git.cacert.org/cacert-gosigner/internal/health" "git.cacert.org/cacert-gosigner/internal/health"
"git.cacert.org/cacert-gosigner/pkg/messages"
) )
var ( var (
@ -55,6 +55,56 @@ type caFile struct {
label string label string
} }
func (c *caFile) buildCertificatePath(caDirectory string) string {
fileName := c.sc.CertificateFileName(c.label)
if caDirectory == "" {
return fileName
}
return path.Join(caDirectory, fileName)
}
func (c *caFile) loadCertificate(caDirectory string) (*x509.Certificate, error) {
certFile := c.buildCertificatePath(caDirectory)
certFileInfo, err := os.Stat(certFile)
if err != nil {
if errors.Is(err, syscall.ENOENT) {
return nil, nil
}
return nil, fmt.Errorf("could not get info for %s: %w", certFile, err)
}
if !certFileInfo.Mode().IsRegular() {
return nil, fmt.Errorf("certificate file %s is not a regular file", certFile)
}
certData, err := os.ReadFile(certFile)
if err != nil {
return nil, fmt.Errorf("could not read %s: %w", certFile, err)
}
certificate, err := x509.ParseCertificate(certData)
if err != nil {
return nil, fmt.Errorf("could not parse certificate from %s: %w", certFile, err)
}
return certificate, nil
}
func (c *caFile) storeCertificate(caDirectory string, certificate []byte) error {
certFile := c.buildCertificatePath(caDirectory)
err := os.WriteFile(certFile, certificate, 0o600)
if err != nil {
return fmt.Errorf("could not write certificate file %s: %w", certFile, err)
}
return nil
}
type Access struct { type Access struct {
logger *logrus.Logger logger *logrus.Logger
caDirectory string caDirectory string
@ -70,33 +120,24 @@ func (a *Access) Healthy() (*health.Info, error) {
moreInfo := make(map[string]string) moreInfo := make(map[string]string)
for _, ca := range a.signerConfig.RootCAs() { for _, ca := range a.signerConfig.RootCAs() {
infoKey := fmt.Sprintf("root-%s", ca) _, err := a.GetRootCACertificate(ca)
cert, err := a.GetRootCACertificate(ca)
if err != nil { if err != nil {
healthy = false healthy = false
moreInfo[infoKey] = messages.CertificateInfoFailed.String() moreInfo[ca] = string(messages.CertStatusFailed)
continue continue
} }
moreInfo[infoKey] = messages.CertificateInfo{ moreInfo[ca] = string(messages.CertStatusOk)
Status: messages.CertStatusOk,
Signing: false,
Profiles: []messages.CAProfile{},
ValidUntil: cert.NotAfter.UTC(),
}.String()
} }
for _, ca := range a.signerConfig.SubordinateCAs() { for _, ca := range a.signerConfig.SubordinateCAs() {
infoKey := fmt.Sprintf("sub-%s", ca) _, err := a.GetSubordinateCACertificate(ca)
cert, err := a.GetSubordinateCACertificate(ca)
if err != nil { if err != nil {
healthy = false healthy = false
moreInfo[infoKey] = messages.CertificateInfoFailed.String() moreInfo[ca] = string(messages.CertStatusFailed)
continue continue
} }
@ -105,7 +146,7 @@ func (a *Access) Healthy() (*health.Info, error) {
if err != nil { if err != nil {
healthy = false healthy = false
moreInfo[infoKey] = messages.CertificateInfoFailed.String() moreInfo[ca] = string(messages.CertStatusFailed)
continue continue
} }
@ -114,17 +155,12 @@ func (a *Access) Healthy() (*health.Info, error) {
if err != nil { if err != nil {
healthy = false healthy = false
moreInfo[infoKey] = messages.CertificateInfoFailed.String() moreInfo[ca] = string(messages.CertStatusFailed)
continue continue
} }
moreInfo[infoKey] = messages.CertificateInfo{ moreInfo[ca] = string(messages.CertStatusOk)
Status: messages.CertStatusOk,
Signing: true,
Profiles: def.Profiles,
ValidUntil: cert.NotAfter.UTC(),
}.String()
} }
return &health.Info{ return &health.Info{
@ -141,54 +177,25 @@ func NewAccess(logger *logrus.Logger, options ...ConfigOption) (*Access, error)
return access, nil return access, nil
} }
func (c *caFile) buildCertificatePath(caDirectory string) string { func (a *Access) GetCAInfo(name string) (*cainfo.Result, error) {
fileName := c.sc.CertificateFileName(c.label) sc := a.GetSignerConfig()
if caDirectory == "" {
return fileName
}
return path.Join(caDirectory, fileName)
}
func (c *caFile) loadCertificate(caDirectory string) (*x509.Certificate, error) {
certFile := c.buildCertificatePath(caDirectory)
certFileInfo, err := os.Stat(certFile)
if err != nil {
if errors.Is(err, syscall.ENOENT) {
return nil, nil
}
return nil, fmt.Errorf("could not get info for %s: %w", certFile, err)
}
if !certFileInfo.Mode().IsRegular() {
return nil, fmt.Errorf("certificate file %s is not a regular file", certFile)
}
certData, err := os.ReadFile(certFile) caFile := &caFile{sc: sc, label: name}
if err != nil {
return nil, fmt.Errorf("could not read %s: %w", certFile, err)
}
certificate, err := x509.ParseCertificate(certData) def, err := sc.GetCADefinition(name)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not parse certificate from %s: %w", certFile, err) return nil, fmt.Errorf("certificate %s unknown: %w", name, err)
} }
return certificate, nil certificate, err := caFile.loadCertificate(a.caDirectory)
}
func (c *caFile) storeCertificate(caDirectory string, certificate []byte) error {
certFile := c.buildCertificatePath(caDirectory)
err := os.WriteFile(certFile, certificate, 0o600)
if err != nil { if err != nil {
return fmt.Errorf("could not write certificate file %s: %w", certFile, err) return nil, fmt.Errorf("certificate %s not available: %w", name, err)
} }
return nil return &cainfo.Result{
Certificate: certificate.Raw,
Profiles: def.Profiles,
}, nil
} }
func (a *Access) GetRootCACertificate(label string) (*x509.Certificate, error) { func (a *Access) GetRootCACertificate(label string) (*x509.Certificate, error) {

@ -22,7 +22,6 @@ package messages
import ( import (
"crypto/x509" "crypto/x509"
"encoding/json"
"encoding/pem" "encoding/pem"
"fmt" "fmt"
"math/big" "math/big"
@ -40,13 +39,21 @@ type CommandCode int8
const ( const (
CmdUndef CommandCode = iota CmdUndef CommandCode = iota
CmdHealth CmdHealth
CmdCAInfo
CmdFetchCRL CmdFetchCRL
CmdSignCertificate
CmdSignOpenPGP
CmdRevokeCertificate
) )
var commandNames = map[CommandCode]string{ var commandNames = map[CommandCode]string{
CmdUndef: "UNDEFINED", CmdUndef: "UNDEFINED",
CmdHealth: "HEALTH", CmdHealth: "HEALTH",
CmdFetchCRL: "FETCH CRL", CmdFetchCRL: "FETCH CRL",
CmdCAInfo: "CA INFO",
CmdSignCertificate: "SIG CERT",
CmdSignOpenPGP: "SIG OPENPGP",
CmdRevokeCertificate: "REV CERT",
} }
func (c CommandCode) String() string { func (c CommandCode) String() string {
@ -63,14 +70,22 @@ const (
RespError ResponseCode = -1 RespError ResponseCode = -1
RespUndef ResponseCode = iota RespUndef ResponseCode = iota
RespHealth RespHealth
RespCAInfo
RespFetchCRL RespFetchCRL
RespSignCertificate
RespSignOpenPGP
RespRevokeCertificate
) )
var responseNames = map[ResponseCode]string{ var responseNames = map[ResponseCode]string{
RespError: "ERROR", RespError: "ERROR",
RespUndef: "UNDEFINED", RespUndef: "UNDEFINED",
RespHealth: "HEALTH", RespHealth: "HEALTH",
RespFetchCRL: "FETCH CRL", RespCAInfo: "CA INFO",
RespFetchCRL: "FETCH CRL",
RespSignCertificate: "SIG CERT",
RespSignOpenPGP: "SIG OPENPGP",
RespRevokeCertificate: "REV CERT",
} }
func (c ResponseCode) String() string { func (c ResponseCode) String() string {
@ -111,13 +126,20 @@ func BuildResponseAnnounce(code ResponseCode, commandID string) *ResponseAnnounc
return &ResponseAnnounce{Code: code, ID: commandID, Created: time.Now().UTC()} return &ResponseAnnounce{Code: code, ID: commandID, Created: time.Now().UTC()}
} }
type HealthCommand struct { type HealthCommand struct{}
}
func (h *HealthCommand) String() string { func (h *HealthCommand) String() string {
return "" return ""
} }
type CAInfoCommand struct {
Name string `msgpack:"name"`
}
func (r *CAInfoCommand) String() string {
return fmt.Sprintf("name=%s", r.Name)
}
type FetchCRLCommand struct { type FetchCRLCommand struct {
IssuerID string `msgpack:"issuer_id"` IssuerID string `msgpack:"issuer_id"`
LastKnownID []byte `msgpack:"last_known_id"` LastKnownID []byte `msgpack:"last_known_id"`
@ -226,8 +248,8 @@ func ParseUsage(u string) (ProfileUsage, error) {
} }
type CAProfile struct { type CAProfile struct {
Name string `json:"name"` Name string `msgpack:"name"`
UseFor ProfileUsage `json:"use-for"` UseFor ProfileUsage `msgpack:"use-for"`
} }
func (p CAProfile) String() string { func (p CAProfile) String() string {
@ -241,33 +263,15 @@ const (
CertStatusFailed CertificateStatus = "failed" CertStatusFailed CertificateStatus = "failed"
) )
type CertificateInfo struct { type CAInfoResponse struct {
Status CertificateStatus `json:"status"` Name string `msgpack:"name"`
Signing bool `json:"signing"` Certificate []byte `msgpack:"certificate"`
Profiles []CAProfile `json:"profiles,omitempty"` Signing bool `msgpack:"signing"`
ValidUntil time.Time `json:"valid-until"` Profiles []CAProfile `msgpack:"profiles,omitempty"`
}
func (i CertificateInfo) String() string {
marshal, _ := json.Marshal(i)
return string(marshal)
}
var CertificateInfoFailed = CertificateInfo{
Status: CertStatusFailed,
Signing: false,
} }
func ParseCertificateInfo(info string) (*CertificateInfo, error) { func (i CAInfoResponse) String() string {
certInfo := CertificateInfo{} return fmt.Sprintf("certificate name=%s, signing=%t, profiles=[%s]", i.Name, i.Signing, i.Profiles)
err := json.Unmarshal([]byte(info), &certInfo)
if err != nil {
return nil, fmt.Errorf("could not parse certificate information: %w", err)
}
return &certInfo, nil
} }
type HealthInfo struct { type HealthInfo struct {

@ -18,6 +18,7 @@ limitations under the License.
package messages package messages
import ( import (
"crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
@ -328,81 +329,38 @@ func TestCAProfile_String(t *testing.T) {
} }
} }
func TestCertificateInfo_String(t *testing.T) { func TestCAInfoCommand_String(t *testing.T) {
infoInstances := []struct { c := CAInfoCommand{Name: "test"}
Name string
info CertificateInfo
}{
{"failed", CertificateInfoFailed},
{"ok",
CertificateInfo{
Status: CertStatusOk,
Signing: true,
Profiles: []CAProfile{{
Name: "test",
UseFor: UsageServer,
}},
ValidUntil: time.Now().UTC(),
},
},
}
for _, i := range infoInstances { str := c.String()
t.Run(i.Name, func(t *testing.T) {
str := i.info.String()
assert.NotEmpty(t, str) assert.NotEmpty(t, str)
assert.Contains(t, str, fmt.Sprintf("%t", i.info.Signing)) assert.Contains(t, str, "test")
assert.True(t, strings.HasPrefix(str, "{")) assert.Contains(t, str, "name")
assert.True(t, strings.HasSuffix(str, "}"))
assert.Contains(t, str, "status")
assert.Contains(t, str, "signing")
if len(i.info.Profiles) == 0 {
assert.NotContains(t, str, "profiles")
} else {
assert.Contains(t, str, "profiles")
}
if i.info.ValidUntil.IsZero() {
assert.Contains(t, str, `"valid-until":"0001-01-01T00:00:00Z"`)
} else {
assert.Contains(t, str, "valid-until")
}
})
}
} }
func TestParseCertificateInfo(t *testing.T) { func TestCAInfoResponse_String(t *testing.T) {
brokenVariants := []struct{ Name, Value string }{ _, certBytes := createTestCertificate(t)
{"no json", "no json for you"},
{"invalid-status", `{"status":"foo"`}, info := CAInfoResponse{
{"invalid-date", `{"status":"failed","valid-until":"foo"`}, Name: "testca",
Signing: true,
Profiles: []CAProfile{{
Name: "test",
UseFor: UsageServer,
}},
Certificate: certBytes,
} }
for _, v := range brokenVariants { str := info.String()
t.Run(v.Name, func(t *testing.T) {
i, err := ParseCertificateInfo(v.Value)
assert.Error(t, err)
assert.Nil(t, i)
})
}
okVariants := []struct{ Name, Value string }{
{"minimal", `{"status":"failed"}`},
{"signing", `{"status":"ok","signing":true}`},
{"with-date", `{"status":"ok","signing":false,"valid-until":"2022-12-31T23:59:59+01:00"}`},
}
for _, v := range okVariants { assert.NotEmpty(t, str)
t.Run(v.Name, func(t *testing.T) { assert.Contains(t, str, fmt.Sprintf("signing=%t", info.Signing))
i, err := ParseCertificateInfo(v.Value) assert.Contains(t, str, "certificate name")
assert.Contains(t, str, "profiles")
assert.NoError(t, err) if len(info.Profiles) == 0 {
require.NotNil(t, i) assert.Contains(t, str, "[]")
assert.NotEmpty(t, i.Status)
})
} }
} }
@ -545,27 +503,7 @@ func TestFetchCRLResponse_String(t *testing.T) {
}) })
t.Run("good-crl", func(t *testing.T) { t.Run("good-crl", func(t *testing.T) {
keyPair, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) keyPair, certBytes := createTestCertificate(t)
require.NoError(t, err)
certTemplate := &x509.Certificate{
BasicConstraintsValid: true,
IsCA: true,
Issuer: pkix.Name{Country: []string{"un"}, Organization: []string{"Acme Ltd."}, CommonName: "foo CA"},
KeyUsage: x509.KeyUsageCRLSign | x509.KeyUsageCertSign,
MaxPathLen: 0,
MaxPathLenZero: false,
NotAfter: time.Now().AddDate(5, 0, 0),
NotBefore: time.Now(),
SerialNumber: big.NewInt(1),
SignatureAlgorithm: x509.ECDSAWithSHA256,
Subject: pkix.Name{Country: []string{"un"}, Organization: []string{"Acme Ltd."}, CommonName: "foo CA"},
}
certBytes, err := x509.CreateCertificate(rand.Reader, certTemplate, certTemplate, keyPair.Public(), keyPair)
require.NoError(t, err)
cert, _ := x509.ParseCertificate(certBytes) cert, _ := x509.ParseCertificate(certBytes)
@ -597,6 +535,34 @@ func TestFetchCRLResponse_String(t *testing.T) {
}) })
} }
func createTestCertificate(t *testing.T) (crypto.Signer, []byte) {
t.Helper()
keyPair, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
certTemplate := &x509.Certificate{
BasicConstraintsValid: true,
IsCA: true,
Issuer: pkix.Name{Country: []string{"un"}, Organization: []string{"Acme Ltd."}, CommonName: "foo CA"},
KeyUsage: x509.KeyUsageCRLSign | x509.KeyUsageCertSign,
MaxPathLen: 0,
MaxPathLenZero: false,
NotAfter: time.Now().AddDate(5, 0, 0),
NotBefore: time.Now(),
SerialNumber: big.NewInt(1),
SignatureAlgorithm: x509.ECDSAWithSHA256,
Subject: pkix.Name{Country: []string{"un"}, Organization: []string{"Acme Ltd."}, CommonName: "foo CA"},
}
certBytes, err := x509.CreateCertificate(rand.Reader, certTemplate, certTemplate, keyPair.Public(), keyPair)
require.NoError(t, err)
return keyPair, certBytes
}
func TestErrorResponse_String(t *testing.T) { func TestErrorResponse_String(t *testing.T) {
r := &ErrorResponse{Message: "kaput"} r := &ErrorResponse{Message: "kaput"}

@ -56,6 +56,36 @@ func ___encodeAsArray(i interface{}) ([]byte, error) {
return nil, fmt.Errorf("%s size / offset different %d : %d", "HealthCommand", size, offset) return nil, fmt.Errorf("%s size / offset different %d : %d", "HealthCommand", size, offset)
} }
return b, err return b, err
case CAInfoCommand:
encoder := enc.NewEncoder()
size, err := ___calcArraySizeCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, encoder)
if err != nil {
return nil, err
}
encoder.MakeBytes(size)
b, offset, err := ___encodeArrayCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, encoder, 0)
if err != nil {
return nil, err
}
if size != offset {
return nil, fmt.Errorf("%s size / offset different %d : %d", "CAInfoCommand", size, offset)
}
return b, err
case *CAInfoCommand:
encoder := enc.NewEncoder()
size, err := ___calcArraySizeCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(*v, encoder)
if err != nil {
return nil, err
}
encoder.MakeBytes(size)
b, offset, err := ___encodeArrayCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(*v, encoder, 0)
if err != nil {
return nil, err
}
if size != offset {
return nil, fmt.Errorf("%s size / offset different %d : %d", "CAInfoCommand", size, offset)
}
return b, err
case FetchCRLCommand: case FetchCRLCommand:
encoder := enc.NewEncoder() encoder := enc.NewEncoder()
size, err := ___calcArraySizeFetchCRLCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, encoder) size, err := ___calcArraySizeFetchCRLCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, encoder)
@ -243,6 +273,36 @@ func ___encodeAsMap(i interface{}) ([]byte, error) {
return nil, fmt.Errorf("%s size / offset different %d : %d", "HealthCommand", size, offset) return nil, fmt.Errorf("%s size / offset different %d : %d", "HealthCommand", size, offset)
} }
return b, err return b, err
case CAInfoCommand:
encoder := enc.NewEncoder()
size, err := ___calcMapSizeCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, encoder)
if err != nil {
return nil, err
}
encoder.MakeBytes(size)
b, offset, err := ___encodeMapCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, encoder, 0)
if err != nil {
return nil, err
}
if size != offset {
return nil, fmt.Errorf("%s size / offset different %d : %d", "CAInfoCommand", size, offset)
}
return b, err
case *CAInfoCommand:
encoder := enc.NewEncoder()
size, err := ___calcMapSizeCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(*v, encoder)
if err != nil {
return nil, err
}
encoder.MakeBytes(size)
b, offset, err := ___encodeMapCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(*v, encoder, 0)
if err != nil {
return nil, err
}
if size != offset {
return nil, fmt.Errorf("%s size / offset different %d : %d", "CAInfoCommand", size, offset)
}
return b, err
case FetchCRLCommand: case FetchCRLCommand:
encoder := enc.NewEncoder() encoder := enc.NewEncoder()
size, err := ___calcMapSizeFetchCRLCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, encoder) size, err := ___calcMapSizeFetchCRLCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, encoder)
@ -423,6 +483,20 @@ func ___decodeAsArray(data []byte, i interface{}) (bool, error) {
return true, fmt.Errorf("read length is different [%d] [%d] ", offset, decoder.Len()) return true, fmt.Errorf("read length is different [%d] [%d] ", offset, decoder.Len())
} }
return true, err return true, err
case *CAInfoCommand:
decoder := dec.NewDecoder(data)
offset, err := ___decodeArrayCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, decoder, 0)
if err == nil && offset != decoder.Len() {
return true, fmt.Errorf("read length is different [%d] [%d] ", offset, decoder.Len())
}
return true, err
case **CAInfoCommand:
decoder := dec.NewDecoder(data)
offset, err := ___decodeArrayCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(*v, decoder, 0)
if err == nil && offset != decoder.Len() {
return true, fmt.Errorf("read length is different [%d] [%d] ", offset, decoder.Len())
}
return true, err
case *FetchCRLCommand: case *FetchCRLCommand:
decoder := dec.NewDecoder(data) decoder := dec.NewDecoder(data)
offset, err := ___decodeArrayFetchCRLCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, decoder, 0) offset, err := ___decodeArrayFetchCRLCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, decoder, 0)
@ -514,6 +588,20 @@ func ___decodeAsMap(data []byte, i interface{}) (bool, error) {
return true, fmt.Errorf("read length is different [%d] [%d] ", offset, decoder.Len()) return true, fmt.Errorf("read length is different [%d] [%d] ", offset, decoder.Len())
} }
return true, err return true, err
case *CAInfoCommand:
decoder := dec.NewDecoder(data)
offset, err := ___decodeMapCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, decoder, 0)
if err == nil && offset != decoder.Len() {
return true, fmt.Errorf("read length is different [%d] [%d] ", offset, decoder.Len())
}
return true, err
case **CAInfoCommand:
decoder := dec.NewDecoder(data)
offset, err := ___decodeMapCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(*v, decoder, 0)
if err == nil && offset != decoder.Len() {
return true, fmt.Errorf("read length is different [%d] [%d] ", offset, decoder.Len())
}
return true, err
case *FetchCRLCommand: case *FetchCRLCommand:
decoder := dec.NewDecoder(data) decoder := dec.NewDecoder(data)
offset, err := ___decodeMapFetchCRLCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, decoder, 0) offset, err := ___decodeMapFetchCRLCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, decoder, 0)
@ -663,6 +751,107 @@ func ___decodeMapHealthCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f8
return offset, err return offset, err
} }
// calculate size from git.cacert.org/cacert-gosigner/pkg/messages.CAInfoCommand
func ___calcArraySizeCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v CAInfoCommand, encoder *enc.Encoder) (int, error) {
size := 0
size += encoder.CalcStructHeaderFix(1)
size += encoder.CalcString(v.Name)
return size, nil
}
// calculate size from git.cacert.org/cacert-gosigner/pkg/messages.CAInfoCommand
func ___calcMapSizeCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v CAInfoCommand, encoder *enc.Encoder) (int, error) {
size := 0
size += encoder.CalcStructHeaderFix(1)
size += encoder.CalcStringFix(4)
size += encoder.CalcString(v.Name)
return size, nil
}
// encode from git.cacert.org/cacert-gosigner/pkg/messages.CAInfoCommand
func ___encodeArrayCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v CAInfoCommand, encoder *enc.Encoder, offset int) ([]byte, int, error) {
var err error
offset = encoder.WriteStructHeaderFixAsArray(1, offset)
offset = encoder.WriteString(v.Name, offset)
return encoder.EncodedBytes(), offset, err
}
// encode from git.cacert.org/cacert-gosigner/pkg/messages.CAInfoCommand
func ___encodeMapCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v CAInfoCommand, encoder *enc.Encoder, offset int) ([]byte, int, error) {
var err error
offset = encoder.WriteStructHeaderFixAsMap(1, offset)
offset = encoder.WriteStringFix("name", 4, offset)
offset = encoder.WriteString(v.Name, offset)
return encoder.EncodedBytes(), offset, err
}
// decode to git.cacert.org/cacert-gosigner/pkg/messages.CAInfoCommand
func ___decodeArrayCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v *CAInfoCommand, decoder *dec.Decoder, offset int) (int, error) {
offset, err := decoder.CheckStructHeader(1, offset)
if err != nil {
return 0, err
}
{
var vv string
vv, offset, err = decoder.AsString(offset)
if err != nil {
return 0, err
}
v.Name = vv
}
return offset, err
}
// decode to git.cacert.org/cacert-gosigner/pkg/messages.CAInfoCommand
func ___decodeMapCAInfoCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v *CAInfoCommand, decoder *dec.Decoder, offset int) (int, error) {
keys := [][]byte{
{uint8(0x6e), uint8(0x61), uint8(0x6d), uint8(0x65)}, // name
}
offset, err := decoder.CheckStructHeader(1, offset)
if err != nil {
return 0, err
}
count := 0
for count < 1 {
var dataKey []byte
dataKey, offset, err = decoder.AsStringBytes(offset)
if err != nil {
return 0, err
}
fieldIndex := -1
for i, key := range keys {
if len(dataKey) != len(key) {
continue
}
fieldIndex = i
for dataKeyIndex := range dataKey {
if dataKey[dataKeyIndex] != key[dataKeyIndex] {
fieldIndex = -1
break
}
}
if fieldIndex >= 0 {
break
}
}
switch fieldIndex {
case 0:
{
var vv string
vv, offset, err = decoder.AsString(offset)
if err != nil {
return 0, err
}
v.Name = vv
}
count++
default:
return 0, fmt.Errorf("unknown key[%s] found", string(dataKey))
}
}
return offset, err
}
// calculate size from git.cacert.org/cacert-gosigner/pkg/messages.FetchCRLCommand // calculate size from git.cacert.org/cacert-gosigner/pkg/messages.FetchCRLCommand
func ___calcArraySizeFetchCRLCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v FetchCRLCommand, encoder *enc.Encoder) (int, error) { func ___calcArraySizeFetchCRLCommand_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v FetchCRLCommand, encoder *enc.Encoder) (int, error) {
size := 0 size := 0

Loading…
Cancel
Save