From 510ba2ad2570b6814a9af3fdc142f42f7fad5acd Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 24 Apr 2022 14:05:46 +0200 Subject: [PATCH] Add test for pkg/hsm/context.go --- .golangci.yml | 2 +- pkg/config/config.go | 14 ++- pkg/config/config_test.go | 20 ++++ pkg/hsm/context.go | 2 +- pkg/hsm/context_test.go | 197 ++++++++++++++++++++++++++++++++++++++ pkg/hsm/storage.go | 10 +- 6 files changed, 236 insertions(+), 9 deletions(-) create mode 100644 pkg/hsm/context_test.go diff --git a/.golangci.yml b/.golangci.yml index 62d5dda..c6859aa 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -33,7 +33,7 @@ linters-settings: gomnd: ignored-functions: - 'strconv.*' - ignored-numbers: ["2", "16", "128", "0o600"] + ignored-numbers: ["2", "16", "128", "0o600", "0o700"] goimports: local-prefixes: code.cacert.org,git.cacert.org misspell: diff --git a/pkg/config/config.go b/pkg/config/config.go index 8899708..147a7b6 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -170,7 +170,7 @@ func (k *KeyStorage) UnmarshalYAML(n *yaml.Node) error { type SignerConfig struct { global *Settings `yaml:"Settings"` caMap map[string]*CaCertificateEntry `yaml:"CAs"` - KeyStorage map[string]*KeyStorage `yaml:"KeyStorage"` + keyStorage map[string]*KeyStorage `yaml:"KeyStorage"` } func (c *SignerConfig) GetCADefinition(label string) (*CaCertificateEntry, error) { @@ -268,6 +268,16 @@ func (c *SignerConfig) IntermediaryCAs() []string { return intermediaries } +func (c *SignerConfig) GetKeyStorage(label string) (*KeyStorage, error) { + keyStorage, ok := c.keyStorage[label] + + if !ok { + return nil, fmt.Errorf("could not find storage definition with label %s", label) + } + + return keyStorage, nil +} + // LoadConfiguration reads YAML configuration from the given reader as a SignerConfig structure func LoadConfiguration(r io.Reader) (*SignerConfig, error) { config := struct { @@ -298,7 +308,7 @@ func LoadConfiguration(r io.Reader) (*SignerConfig, error) { return &SignerConfig{ global: config.Global, caMap: config.CAs, - KeyStorage: config.KeyStorage, + keyStorage: config.KeyStorage, }, nil } diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 9f34556..c5beea0 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -954,3 +954,23 @@ func TestSignerConfig_CalculateSubject(t *testing.T) { }) } } + +func TestSignerConfig_GetKeyStorage(t *testing.T) { + var ( + keyStorage *config.KeyStorage + err error + ) + + sc := loadSignerConfig(t) + + keyStorage, err = sc.GetKeyStorage("default") + + assert.NoError(t, err) + assert.NotNil(t, keyStorage) + + keyStorage, err = sc.GetKeyStorage("undefined") + + assert.Error(t, err) + assert.ErrorContains(t, err, "could not find storage definition with label") + assert.Nil(t, keyStorage) +} diff --git a/pkg/hsm/context.go b/pkg/hsm/context.go index 83aa903..8bc0f2a 100644 --- a/pkg/hsm/context.go +++ b/pkg/hsm/context.go @@ -98,7 +98,7 @@ func IsVerbose(ctx context.Context) bool { func GetP11Context(ctx context.Context, entry *config.CaCertificateEntry) (*crypto11.Context, error) { contexts, ok := ctx.Value(ctxP11Contexts).(map[string]*crypto11.Context) if !ok { - return nil, errors.New("type assertion failed") + return nil, errors.New("type assertion failed, use hsm.SetupContext first") } if p11Context, ok := contexts[entry.Storage]; ok { diff --git a/pkg/hsm/context_test.go b/pkg/hsm/context_test.go new file mode 100644 index 0000000..ed5c603 --- /dev/null +++ b/pkg/hsm/context_test.go @@ -0,0 +1,197 @@ +/* +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 hsm_test + +import ( + "context" + "fmt" + "os" + "os/exec" + "path" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "git.cacert.org/cacert-gosigner/pkg/config" + "git.cacert.org/cacert-gosigner/pkg/hsm" +) + +func TestCaConfigOption(t *testing.T) { + testSignerConfig := config.SignerConfig{} + + theContext := hsm.SetupContext(hsm.CaConfigOption(&testSignerConfig)) + + assert.Equal(t, &testSignerConfig, hsm.GetSignerConfig(theContext)) +} + +func TestGetSignerConfig_empty(t *testing.T) { + theContext := hsm.SetupContext() + + assert.Nil(t, hsm.GetSignerConfig(theContext)) +} + +func TestSetupModeOption(t *testing.T) { + theContext := hsm.SetupContext(hsm.SetupModeOption()) + + assert.True(t, hsm.IsSetupMode(theContext)) +} + +func TestIsSetupMode_not_set(t *testing.T) { + theContext := hsm.SetupContext() + + assert.False(t, hsm.IsSetupMode(theContext)) +} + +func TestVerboseLoggingOption(t *testing.T) { + theContext := hsm.SetupContext(hsm.VerboseLoggingOption()) + + assert.True(t, hsm.IsVerbose(theContext)) +} + +func TestIsVerbose_not_set(t *testing.T) { + theContext := hsm.SetupContext() + + assert.False(t, hsm.IsVerbose(theContext)) +} + +const testSignerConfig = `--- +Settings: + organization: + organization: ["Acme CAs Ltd."] + validity-years: + root: 30 + intermediary: 10 + url-patterns: + ocsp: http://ocsp.example.org/ + crl: http://crl.example.org/%s.crl + issuer: http://%s.cas.example.org/ +CAs: + root: + common-name: "Acme CAs root" + key-info: + algorithm: EC + ecc-curve: secp521r1 + sub1: + common-name: "Acme CAs server sub CA" + parent: root + key-info: + algorithm: EC + ecc-curve: secp256r1 + sub2: + common-name: "Acme CAs people sub CA" + parent: root + key-info: + algorithm: EC + ecc-curve: secp256r1 +KeyStorage: + default: + type: softhsm + label: acme-test-hsm +` + +func TestSetupContext(t *testing.T) { + testConfig, err := config.LoadConfiguration(strings.NewReader(testSignerConfig)) + + require.NoError(t, err) + + theContext := hsm.SetupContext(hsm.SetupModeOption(), hsm.VerboseLoggingOption(), hsm.CaConfigOption(testConfig)) + + assert.True(t, hsm.IsSetupMode(theContext)) + assert.True(t, hsm.IsVerbose(theContext)) + assert.Equal(t, hsm.GetSignerConfig(theContext), testConfig) +} + +func TestGetP11Context_missing_SetupContext(t *testing.T) { + p11Context, err := hsm.GetP11Context(context.Background(), &config.CaCertificateEntry{Storage: "default"}) + + assert.Error(t, err) + assert.ErrorContains(t, err, "type assertion failed, use hsm.SetupContext first") + assert.Nil(t, p11Context) +} + +func TestGetP11Context_unknown_storage(t *testing.T) { + testConfig, err := config.LoadConfiguration(strings.NewReader(testSignerConfig)) + + require.NoError(t, err) + + theContext := hsm.SetupContext(hsm.SetupModeOption(), hsm.CaConfigOption(testConfig)) + + definition := &config.CaCertificateEntry{Storage: "undefined"} + + p11Context, err := hsm.GetP11Context(theContext, definition) + + assert.Error(t, err) + assert.ErrorContains(t, err, "key storage undefined not available") + assert.Nil(t, p11Context) +} + +func TestGetP11Context(t *testing.T) { + testConfig, err := config.LoadConfiguration(strings.NewReader(testSignerConfig)) + + require.NoError(t, err) + + tempdir := t.TempDir() + + tokenDir := path.Join(tempdir, "tokens") + softhsmConfig := path.Join(tempdir, "softhsm2.conf") + + err = os.Mkdir(tokenDir, 0o700) + + require.NoError(t, err) + + err = os.WriteFile(softhsmConfig, []byte(fmt.Sprintf("directories.tokendir = %s", tokenDir)), 0o600) + + require.NoError(t, err) + + t.Setenv("SOFTHSM2_CONF", softhsmConfig) + + err = exec.Command( + "softhsm2-util", + "--init-token", + "--free", + "--label", + "acme-test-hsm", + "--so-pin", + "12345678", + "--pin", + "123456", + ).Run() + + require.NoError(t, err) + + t.Setenv("TOKEN_PIN_ACME_TEST_HSM", "123456") + + theContext := hsm.SetupContext(hsm.CaConfigOption(testConfig)) + + definition, err := testConfig.GetCADefinition("root") + + require.NoError(t, err) + + p11Context1, err := hsm.GetP11Context(theContext, definition) + + assert.NoError(t, err) + assert.NotNil(t, p11Context1) + + p11Context2, err := hsm.GetP11Context(theContext, definition) + + assert.NoError(t, err) + assert.NotNil(t, p11Context1) + assert.Equal(t, p11Context1, p11Context2) +} diff --git a/pkg/hsm/storage.go b/pkg/hsm/storage.go index 1605819..2c762e4 100644 --- a/pkg/hsm/storage.go +++ b/pkg/hsm/storage.go @@ -31,16 +31,16 @@ import ( ) func prepareCrypto11Context(ctx context.Context, label string) (*crypto11.Context, error) { - storage, ok := GetSignerConfig(ctx).KeyStorage[label] - if !ok { - return nil, fmt.Errorf("could not find storage definition with label %s", label) - } - var ( err error p11Context *crypto11.Context ) + storage, err := GetSignerConfig(ctx).GetKeyStorage(label) + if err != nil { + return nil, fmt.Errorf("key storage %s not available: %w", label, err) + } + p11Config := &crypto11.Config{ Path: storage.Module, TokenLabel: storage.Label,