994 lines
20 KiB
Go
994 lines
20 KiB
Go
/*
|
|
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 config_test
|
|
|
|
import (
|
|
"crypto/elliptic"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"math/big"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gopkg.in/yaml.v3"
|
|
|
|
"git.cacert.org/cacert-gosigner/internal/config"
|
|
)
|
|
|
|
type TestCurve struct {
|
|
}
|
|
|
|
func (t TestCurve) Params() *elliptic.CurveParams {
|
|
panic("not needed")
|
|
}
|
|
|
|
func (t TestCurve) IsOnCurve(_, _ *big.Int) bool {
|
|
panic("not needed")
|
|
}
|
|
|
|
func (t TestCurve) Add(_, _, _, _ *big.Int) (x, y *big.Int) {
|
|
panic("not needed")
|
|
}
|
|
|
|
func (t TestCurve) Double(_, _ *big.Int) (x, y *big.Int) {
|
|
panic("not needed")
|
|
}
|
|
|
|
func (t TestCurve) ScalarMult(_, _ *big.Int, _ []byte) (x, y *big.Int) {
|
|
panic("not needed")
|
|
}
|
|
|
|
func (t TestCurve) ScalarBaseMult(_ []byte) (x, y *big.Int) {
|
|
panic("not needed")
|
|
}
|
|
|
|
func TestPrivateKeyInfo_MarshalYAML(t *testing.T) {
|
|
testCurve := TestCurve{}
|
|
testData := []struct {
|
|
name string
|
|
pkInfo *config.PrivateKeyInfo
|
|
expected string
|
|
expectErr bool
|
|
}{
|
|
{
|
|
name: "RSA",
|
|
pkInfo: &config.PrivateKeyInfo{
|
|
Algorithm: x509.RSA,
|
|
RSABits: 3072,
|
|
},
|
|
expected: `algorithm: RSA
|
|
rsa-bits: 3072
|
|
`,
|
|
},
|
|
{
|
|
name: "ECDSA P-224",
|
|
pkInfo: &config.PrivateKeyInfo{
|
|
Algorithm: x509.ECDSA,
|
|
EccCurve: elliptic.P224(),
|
|
},
|
|
expected: `algorithm: EC
|
|
ecc-curve: P-224
|
|
`,
|
|
},
|
|
{
|
|
name: "ECDSA P-256",
|
|
pkInfo: &config.PrivateKeyInfo{
|
|
Algorithm: x509.ECDSA,
|
|
EccCurve: elliptic.P256(),
|
|
},
|
|
expected: `algorithm: EC
|
|
ecc-curve: P-256
|
|
`,
|
|
},
|
|
{
|
|
name: "ECDSA P-384",
|
|
pkInfo: &config.PrivateKeyInfo{
|
|
Algorithm: x509.ECDSA,
|
|
EccCurve: elliptic.P384(),
|
|
},
|
|
expected: `algorithm: EC
|
|
ecc-curve: P-384
|
|
`,
|
|
},
|
|
{
|
|
name: "ECDSA P-521",
|
|
pkInfo: &config.PrivateKeyInfo{
|
|
Algorithm: x509.ECDSA,
|
|
EccCurve: elliptic.P521(),
|
|
},
|
|
expected: `algorithm: EC
|
|
ecc-curve: P-521
|
|
`,
|
|
},
|
|
{
|
|
name: "ECDSA unsupported curve",
|
|
pkInfo: &config.PrivateKeyInfo{
|
|
Algorithm: x509.ECDSA,
|
|
EccCurve: testCurve,
|
|
},
|
|
expectErr: true,
|
|
},
|
|
}
|
|
|
|
for _, item := range testData {
|
|
t.Run(item.name, func(t *testing.T) {
|
|
data, err := yaml.Marshal(item.pkInfo)
|
|
if item.expectErr {
|
|
require.Error(t, err)
|
|
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.YAMLEq(t, item.expected, string(data))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPrivateKeyInfo_UnmarshalYAML(t *testing.T) {
|
|
testData := []struct {
|
|
name string
|
|
yaml string
|
|
expected *config.PrivateKeyInfo
|
|
expectErr bool
|
|
}{
|
|
{
|
|
name: "malformed",
|
|
yaml: `noyaml`,
|
|
expectErr: true,
|
|
},
|
|
{
|
|
name: "RSA",
|
|
yaml: `label: "mykey"
|
|
algorithm: "RSA"
|
|
rsa-bits: 2048`,
|
|
expected: &config.PrivateKeyInfo{
|
|
Algorithm: x509.RSA,
|
|
RSABits: 2048,
|
|
CRLSignatureAlgorithm: x509.SHA256WithRSA,
|
|
},
|
|
},
|
|
{
|
|
name: "ECDSA P-224",
|
|
yaml: `label: "mykey"
|
|
algorithm: "EC"
|
|
ecc-curve: "P-224"`,
|
|
expected: &config.PrivateKeyInfo{
|
|
Algorithm: x509.ECDSA,
|
|
EccCurve: elliptic.P224(),
|
|
CRLSignatureAlgorithm: x509.ECDSAWithSHA256,
|
|
},
|
|
},
|
|
{
|
|
name: "ECDSA P-256",
|
|
yaml: `label: "mykey"
|
|
algorithm: "EC"
|
|
ecc-curve: "P-256"`,
|
|
expected: &config.PrivateKeyInfo{
|
|
Algorithm: x509.ECDSA,
|
|
EccCurve: elliptic.P256(),
|
|
CRLSignatureAlgorithm: x509.ECDSAWithSHA256,
|
|
},
|
|
},
|
|
{
|
|
name: "ECDSA P-384",
|
|
yaml: `label: "mykey"
|
|
algorithm: "EC"
|
|
ecc-curve: "P-384"`,
|
|
expected: &config.PrivateKeyInfo{
|
|
Algorithm: x509.ECDSA,
|
|
EccCurve: elliptic.P384(),
|
|
CRLSignatureAlgorithm: x509.ECDSAWithSHA256,
|
|
},
|
|
},
|
|
{
|
|
name: "ECDSA P-521",
|
|
yaml: `label: "mykey"
|
|
algorithm: "EC"
|
|
ecc-curve: "P-521"`,
|
|
expected: &config.PrivateKeyInfo{
|
|
Algorithm: x509.ECDSA,
|
|
EccCurve: elliptic.P521(),
|
|
CRLSignatureAlgorithm: x509.ECDSAWithSHA256,
|
|
},
|
|
},
|
|
{
|
|
name: "no-algorithm",
|
|
yaml: `label: "mykey"`,
|
|
expectErr: true,
|
|
},
|
|
{
|
|
name: "RSA-no-rsa-bits",
|
|
yaml: `label: "mykey"
|
|
algorithm: "RSA"`,
|
|
expectErr: true,
|
|
},
|
|
{
|
|
name: "ECDSA-no-curve",
|
|
yaml: `label: "mykey"
|
|
algorithm: "EC"`,
|
|
expectErr: true,
|
|
},
|
|
{
|
|
name: "ECDSA-invalid-curve",
|
|
yaml: `label: "mykey"
|
|
algorithm: "EC"
|
|
ecc-curve: "fancy-but-wrong"`,
|
|
expectErr: true,
|
|
},
|
|
{
|
|
name: "wrong-key-algorithm",
|
|
yaml: `label: "mykey"
|
|
algorithm: "ChaCha"`,
|
|
expectErr: true,
|
|
},
|
|
}
|
|
|
|
for _, item := range testData {
|
|
t.Run(item.name, func(t *testing.T) {
|
|
pkInfo := &config.PrivateKeyInfo{}
|
|
err := yaml.Unmarshal([]byte(item.yaml), pkInfo)
|
|
if err != nil && !item.expectErr {
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
if item.expectErr {
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
if !item.expectErr {
|
|
assert.Equal(t, item.expected, pkInfo)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCaCertificateEntry_UnmarshalYAML(t *testing.T) {
|
|
testData := []struct {
|
|
name, yaml string
|
|
expected config.CaCertificateEntry
|
|
expectErr bool
|
|
}{
|
|
{
|
|
name: "no yaml",
|
|
yaml: "no yaml",
|
|
expectErr: true,
|
|
},
|
|
{
|
|
name: "no common name",
|
|
yaml: `
|
|
key-info:
|
|
algorithm: EC
|
|
ecc-curve: P-521
|
|
`,
|
|
expectErr: true,
|
|
},
|
|
{
|
|
name: "no key info",
|
|
yaml: `
|
|
common-name: My Little Test Root CA
|
|
`,
|
|
expectErr: true,
|
|
},
|
|
{
|
|
name: "EC P-521",
|
|
yaml: `
|
|
key-info:
|
|
algorithm: EC
|
|
ecc-curve: P-521
|
|
common-name: My Little Test Root CA
|
|
ext-key-usages:
|
|
- client
|
|
- server
|
|
`,
|
|
expectErr: true,
|
|
},
|
|
{
|
|
name: "EC root P-521",
|
|
yaml: `
|
|
key-info:
|
|
algorithm: EC
|
|
ecc-curve: P-521
|
|
common-name: My Little Test Root CA
|
|
storage: root
|
|
`,
|
|
expected: config.CaCertificateEntry{
|
|
KeyInfo: &config.PrivateKeyInfo{
|
|
Algorithm: x509.ECDSA,
|
|
EccCurve: elliptic.P521(),
|
|
CRLSignatureAlgorithm: x509.ECDSAWithSHA256,
|
|
},
|
|
CommonName: "My Little Test Root CA",
|
|
Storage: "root",
|
|
},
|
|
},
|
|
{
|
|
name: "EC sub P-256 client server",
|
|
yaml: `
|
|
key-info:
|
|
algorithm: EC
|
|
ecc-curve: P-256
|
|
common-name: My Little Test Sub CA
|
|
parent: root
|
|
ext-key-usages:
|
|
- client
|
|
- server
|
|
`,
|
|
expected: config.CaCertificateEntry{
|
|
KeyInfo: &config.PrivateKeyInfo{
|
|
Algorithm: x509.ECDSA,
|
|
EccCurve: elliptic.P256(),
|
|
CRLSignatureAlgorithm: x509.ECDSAWithSHA256,
|
|
},
|
|
Parent: "root",
|
|
CommonName: "My Little Test Sub CA",
|
|
Storage: "default",
|
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
|
x509.ExtKeyUsageClientAuth,
|
|
x509.ExtKeyUsageServerAuth,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "EC sub P-256 person",
|
|
yaml: `
|
|
key-info:
|
|
algorithm: EC
|
|
ecc-curve: P-256
|
|
common-name: My Little Test Sub CA
|
|
parent: root
|
|
ext-key-usages:
|
|
- client
|
|
- code
|
|
- email
|
|
- ocsp
|
|
`,
|
|
expected: config.CaCertificateEntry{
|
|
KeyInfo: &config.PrivateKeyInfo{
|
|
Algorithm: x509.ECDSA,
|
|
EccCurve: elliptic.P256(),
|
|
CRLSignatureAlgorithm: x509.ECDSAWithSHA256,
|
|
},
|
|
CommonName: "My Little Test Sub CA",
|
|
Storage: "default",
|
|
Parent: "root",
|
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
|
x509.ExtKeyUsageClientAuth,
|
|
x509.ExtKeyUsageCodeSigning,
|
|
x509.ExtKeyUsageEmailProtection,
|
|
x509.ExtKeyUsageOCSPSigning,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "invalid ext-key-usages",
|
|
yaml: `
|
|
key-info:
|
|
algorithm: EC
|
|
ecc-curve: P-256
|
|
common-name: My Little Test Sub CA
|
|
parent: root
|
|
ext-key-usages:
|
|
- wrong_one
|
|
`,
|
|
expectErr: true,
|
|
},
|
|
}
|
|
|
|
for _, item := range testData {
|
|
t.Run(item.name, func(t *testing.T) {
|
|
entry := config.CaCertificateEntry{}
|
|
|
|
err := yaml.Unmarshal([]byte(item.yaml), &entry)
|
|
if item.expectErr {
|
|
require.Error(t, err)
|
|
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, item.expected, entry)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLoadConfiguration(t *testing.T) {
|
|
testData := map[string]struct {
|
|
yaml string
|
|
errMsg string
|
|
}{
|
|
"Good": {
|
|
yaml: `---
|
|
Settings:
|
|
organization:
|
|
organization: ["Acme CAs Ltd."]
|
|
validity-years:
|
|
root: 20
|
|
subordinate: 5
|
|
url-patterns:
|
|
ocsp: http://ocsp.example.org/
|
|
crl: http://crl.example.org/%s
|
|
issuer: http://%s.cas.example.org/
|
|
serial:
|
|
device: /dev/ttyUSB0
|
|
KeyStorage:
|
|
default:
|
|
type: softhsm
|
|
label: ca-storage
|
|
CAs:
|
|
root:
|
|
common-name: "Root CA"
|
|
key-info:
|
|
algorithm: EC
|
|
ecc-curve: P-384
|
|
sub1:
|
|
common-name: "Sub CA 1"
|
|
key-info:
|
|
algorithm: EC
|
|
ecc-curve: P-256
|
|
parent: root
|
|
sub2:
|
|
common-name: "Sub CA 2"
|
|
key-info:
|
|
algorithm: EC
|
|
ecc-curve: P-256
|
|
parent: root
|
|
`,
|
|
},
|
|
"Bad": {
|
|
yaml: `no yaml for you: ]`,
|
|
errMsg: "could not parse YAML configuration",
|
|
},
|
|
"NoGlobal": {
|
|
yaml: `---
|
|
KeyStorage:
|
|
default:
|
|
label: default
|
|
type: softhsm
|
|
CAs:
|
|
root:
|
|
common-name: "Root CA"
|
|
key-info:
|
|
algorithm: EC
|
|
ecc-curve: P-384
|
|
`,
|
|
errMsg: "configuration entry 'Settings' is missing or empty",
|
|
},
|
|
"NoKeyStorage": {
|
|
yaml: `
|
|
Settings:
|
|
organization:
|
|
organization: ["Acme CAs Ltd."]
|
|
validity-years:
|
|
root: 20
|
|
subordinate: 5
|
|
url-patterns:
|
|
ocsp: http://ocsp.example.org/
|
|
crl: http://crl.example.org/%s.crl
|
|
issuer: http://%s.cas.example.org/
|
|
serial:
|
|
device: /dev/ttyUSB0
|
|
CAs:
|
|
root:
|
|
common-name: "Root CA"
|
|
key-info:
|
|
algorithm: EC
|
|
ecc-curve: P-384
|
|
`,
|
|
errMsg: "configuration entry 'KeyStorage' is missing or empty",
|
|
},
|
|
"NoCAs": {
|
|
yaml: `
|
|
Settings:
|
|
organization:
|
|
organization: ["Acme CAs Ltd."]
|
|
validity-years:
|
|
root: 20
|
|
subordinate: 5
|
|
url-patterns:
|
|
ocsp: http://ocsp.example.org/
|
|
crl: http://crl.example.org/%s.crl
|
|
issuer: http://%s.cas.example.org/
|
|
serial:
|
|
device: /dev/ttyUSB0
|
|
KeyStorage:
|
|
default:
|
|
label: default
|
|
type: softhsm
|
|
`,
|
|
errMsg: "configuration entry 'CAs' is missing or empty",
|
|
},
|
|
}
|
|
|
|
for name, item := range testData {
|
|
t.Run(name, func(t *testing.T) {
|
|
r := strings.NewReader(item.yaml)
|
|
sc, err := config.LoadConfiguration(r)
|
|
|
|
if item.errMsg != "" {
|
|
assert.ErrorContains(t, err, item.errMsg)
|
|
assert.Nil(t, sc)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, sc)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
const sampleConfig = `---
|
|
Settings:
|
|
organization:
|
|
organization: ["Acme CAs Ltd."]
|
|
validity-years:
|
|
root: 20
|
|
subordinate: 5
|
|
url-patterns:
|
|
ocsp: http://ocsp.example.org/
|
|
crl: http://crl.example.org/%s.crl
|
|
issuer: http://%s.cas.example.org/
|
|
serial:
|
|
device: /dev/ttyUSB0
|
|
KeyStorage:
|
|
default:
|
|
type: softhsm
|
|
label: ca-storage
|
|
CAs:
|
|
root:
|
|
common-name: "Root CA"
|
|
key-info:
|
|
algorithm: EC
|
|
ecc-curve: P-384
|
|
sub1:
|
|
common-name: "Sub CA 1"
|
|
key-info:
|
|
algorithm: EC
|
|
ecc-curve: P-256
|
|
parent: root
|
|
sub2:
|
|
common-name: "Sub CA 2"
|
|
key-info:
|
|
algorithm: EC
|
|
ecc-curve: P-256
|
|
parent: root
|
|
`
|
|
|
|
func loadSignerConfig(t *testing.T) *config.SignerConfig {
|
|
r := strings.NewReader(sampleConfig)
|
|
sc, err := config.LoadConfiguration(r)
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, sc)
|
|
|
|
return sc
|
|
}
|
|
|
|
func TestSignerConfig_RootCAs(t *testing.T) {
|
|
sc := loadSignerConfig(t)
|
|
|
|
roots := sc.RootCAs()
|
|
assert.Equal(t, roots, []string{"root"})
|
|
}
|
|
|
|
func TestSignerConfig_SubordinateCAs(t *testing.T) {
|
|
sc := loadSignerConfig(t)
|
|
|
|
cAs := sc.SubordinateCAs()
|
|
assert.ElementsMatch(t, cAs, []string{"sub1", "sub2"})
|
|
}
|
|
|
|
func TestSignerConfig_GetParentCA(t *testing.T) {
|
|
testData := map[string]struct{ errMsg string }{
|
|
"root": {
|
|
errMsg: "CA root is a root CA and has no parent",
|
|
},
|
|
"unknown": {
|
|
errMsg: "no CA definition for unknown",
|
|
},
|
|
"sub1": {},
|
|
}
|
|
|
|
sc := loadSignerConfig(t)
|
|
|
|
for name, item := range testData {
|
|
t.Run(name, func(t *testing.T) {
|
|
ca, err := sc.GetParentCA(name)
|
|
|
|
if item.errMsg != "" {
|
|
assert.ErrorContains(t, err, item.errMsg)
|
|
} else {
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Root CA", ca.CommonName)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSignerConfig_CertificateFileName(t *testing.T) {
|
|
sc := loadSignerConfig(t)
|
|
|
|
assert.Equal(t, "root.crt", sc.CertificateFileName("root"))
|
|
}
|
|
|
|
func TestSignerConfig_BuildCRLUrl(t *testing.T) {
|
|
sc := loadSignerConfig(t)
|
|
|
|
ca, err := sc.GetCADefinition("sub1")
|
|
require.NoError(t, err)
|
|
|
|
url := sc.BuildCRLUrl(ca)
|
|
assert.Equal(t, "http://crl.example.org/root.crl", url)
|
|
}
|
|
|
|
func TestSignerConfig_BuildIssuerUrl(t *testing.T) {
|
|
sc := loadSignerConfig(t)
|
|
|
|
ca, err := sc.GetCADefinition("sub1")
|
|
require.NoError(t, err)
|
|
|
|
url := sc.BuildIssuerURL(ca)
|
|
assert.Equal(t, "http://root.cas.example.org/", url)
|
|
}
|
|
|
|
func TestSignerConfig_BuildOCSPURL(t *testing.T) {
|
|
sc := loadSignerConfig(t)
|
|
|
|
ca, err := sc.GetCADefinition("sub1")
|
|
require.NoError(t, err)
|
|
|
|
url := sc.BuildOCSPURL(ca)
|
|
assert.Equal(t, "http://ocsp.example.org/", url)
|
|
}
|
|
|
|
func TestSignerConfig_BuildOCSPURL2(t *testing.T) {
|
|
r := strings.NewReader(`---
|
|
Settings:
|
|
organization:
|
|
organization: ["Acme CAs Ltd."]
|
|
validity-years:
|
|
root: 20
|
|
subordinate: 5
|
|
url-patterns:
|
|
ocsp: http://ocsp.example.org/%s
|
|
crl: http://crl.example.org/%s.crl
|
|
issuer: http://%s.cas.example.org/
|
|
serial:
|
|
device: /dev/ttyUSB0
|
|
KeyStorage:
|
|
default:
|
|
type: softhsm
|
|
label: ca-storage
|
|
CAs:
|
|
root:
|
|
common-name: "Root CA"
|
|
key-info:
|
|
algorithm: EC
|
|
ecc-curve: P-384
|
|
sub1:
|
|
common-name: "Sub CA 1"
|
|
key-info:
|
|
algorithm: EC
|
|
ecc-curve: P-256
|
|
parent: root
|
|
sub2:
|
|
common-name: "Sub CA 2"
|
|
key-info:
|
|
algorithm: EC
|
|
ecc-curve: P-256
|
|
parent: root
|
|
`)
|
|
sc, err := config.LoadConfiguration(r)
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, sc)
|
|
|
|
ca, err := sc.GetCADefinition("sub1")
|
|
require.NoError(t, err)
|
|
|
|
url := sc.BuildOCSPURL(ca)
|
|
assert.Equal(t, "http://ocsp.example.org/root", url)
|
|
}
|
|
|
|
func TestSettings_UnmarshalYAML(t *testing.T) {
|
|
testData := map[string]struct {
|
|
yaml string
|
|
expected config.Settings
|
|
errMsg string
|
|
}{
|
|
"no yaml": {
|
|
yaml: "no yaml",
|
|
errMsg: "could not decode YAML",
|
|
},
|
|
"missing organization": {
|
|
yaml: `
|
|
validity-years:
|
|
root: 10
|
|
subordinate: 5
|
|
url-patterns:
|
|
ocsp: http://ocsp.example.org/
|
|
crl: http://crl.example.org/%s.crl
|
|
issuer: http://issuer.example.org/%s.crt
|
|
`,
|
|
errMsg: "invalid Settings you need to specify 'organization'",
|
|
},
|
|
"missing validity-years": {
|
|
yaml: `
|
|
organization:
|
|
organization: ["Acme CAs Ltd."]
|
|
url-patterns:
|
|
ocsp: http://ocsp.example.org/
|
|
crl: http://crl.example.org/%s.crl
|
|
issuer: http://issuer.example.org/%s.crt
|
|
`,
|
|
errMsg: "invalid Settings you must specify validity years for 'root' and 'subordinate'",
|
|
},
|
|
"missing url-patterns": {
|
|
yaml: `
|
|
organization:
|
|
organization: ["Acme CAs Ltd."]
|
|
validity-years:
|
|
root: 10
|
|
subordinate: 5
|
|
`,
|
|
errMsg: "invalid Settings",
|
|
},
|
|
"invalid validity-years": {
|
|
yaml: `
|
|
organization:
|
|
organization: ["Acme CAs Ltd."]
|
|
validity-years:
|
|
root: 5
|
|
subordinate: 10
|
|
url-patterns:
|
|
ocsp: http://ocsp.example.org/
|
|
crl: http://crl.example.org/%s.crl
|
|
issuer: http://issuer.example.org/%s.crt
|
|
`,
|
|
errMsg: "invalid Settings validity of root CA certificates must be equal or greater than" +
|
|
" those of subordinate CA certificates",
|
|
},
|
|
"no OCSP pattern": {
|
|
yaml: `
|
|
organization:
|
|
organization: ["Acme CAs Ltd."]
|
|
validity-years:
|
|
root: 10
|
|
subordinate: 5
|
|
url-patterns:
|
|
crl: http://crl.example.org/%s.crl
|
|
issuer: http://issuer.example.org/%s.crt
|
|
`,
|
|
errMsg: "invalid Settings you must specify an 'ocsp' URL pattern",
|
|
},
|
|
"invalid OCSP pattern": {
|
|
yaml: `
|
|
organization:
|
|
organization: ["Acme CAs Ltd."]
|
|
validity-years:
|
|
root: 10
|
|
subordinate: 5
|
|
url-patterns:
|
|
ocsp: http://ocsp.example.org/%s_%s
|
|
crl: http://crl.example.org/%s.crl
|
|
issuer: http://issuer.example.org/%s.crt
|
|
`,
|
|
errMsg: "invalid Settings url-pattern 'ocsp' must contain zero or one '%s' placeholder",
|
|
},
|
|
"no CRL pattern": {
|
|
yaml: `
|
|
organization:
|
|
organization: ["Acme CAs Ltd."]
|
|
validity-years:
|
|
root: 10
|
|
subordinate: 5
|
|
url-patterns:
|
|
ocsp: http://ocsp.example.org/
|
|
issuer: http://issuer.example.org/%s.crt
|
|
`,
|
|
errMsg: "invalid Settings you must specify an 'crl' URL pattern",
|
|
},
|
|
"invalid CRL pattern": {
|
|
yaml: `
|
|
organization:
|
|
organization: ["Acme CAs Ltd."]
|
|
validity-years:
|
|
root: 10
|
|
subordinate: 5
|
|
url-patterns:
|
|
ocsp: http://ocsp.example.org/
|
|
crl: http://crl.example.org/
|
|
issuer: http://issuer.example.org/%s.crt
|
|
`,
|
|
errMsg: "invalid Settings url-pattern 'crl' must contain one '%s' placeholder",
|
|
},
|
|
"no issuer pattern": {
|
|
yaml: `
|
|
organization:
|
|
organization: ["Acme CAs Ltd."]
|
|
validity-years:
|
|
root: 10
|
|
subordinate: 5
|
|
url-patterns:
|
|
crl: http://crl.example.org/%s.crl
|
|
ocsp: http://ocsp.example.org/
|
|
`,
|
|
errMsg: "invalid Settings you must specify an 'issuer' URL pattern",
|
|
},
|
|
"invalid issuer pattern": {
|
|
yaml: `
|
|
organization:
|
|
organization: ["Acme CAs Ltd."]
|
|
validity-years:
|
|
root: 10
|
|
subordinate: 5
|
|
url-patterns:
|
|
ocsp: http://ocsp.example.org/
|
|
crl: http://crl.example.org/%s.crl
|
|
issuer: http://issuer.example.org/
|
|
`,
|
|
errMsg: "invalid Settings url-pattern 'issuer' must contain one '%s' placeholder",
|
|
},
|
|
}
|
|
|
|
for name, item := range testData {
|
|
t.Run(name, func(t *testing.T) {
|
|
var s config.Settings
|
|
err := yaml.Unmarshal([]byte(item.yaml), &s)
|
|
|
|
if item.errMsg != "" {
|
|
assert.ErrorContains(t, err, item.errMsg)
|
|
} else {
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, s)
|
|
assert.Equal(t, item.expected, s)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestKeyStorage_UnmarshalYAML(t *testing.T) {
|
|
testData := map[string]struct {
|
|
yaml string
|
|
expected config.KeyStorage
|
|
errMsg string
|
|
}{
|
|
"no yaml": {
|
|
yaml: `no yaml`,
|
|
errMsg: "could not decode YAML:",
|
|
},
|
|
"softhsm": {
|
|
yaml: `
|
|
label: softhsm
|
|
type: softhsm`,
|
|
expected: config.KeyStorage{
|
|
Label: "softhsm", Module: config.SoftHsmModule,
|
|
},
|
|
},
|
|
"custom": {
|
|
yaml: `
|
|
label: custom
|
|
type: p11module
|
|
module: /usr/lib/mytokenp11.so`,
|
|
expected: config.KeyStorage{
|
|
Label: "custom", Module: "/usr/lib/mytokenp11.so",
|
|
},
|
|
},
|
|
"missing module": {
|
|
yaml: `
|
|
label: custom
|
|
type: p11module`,
|
|
errMsg: "specify a 'module' field when using the 'p11module' type",
|
|
},
|
|
"unsupported type": {
|
|
yaml: `
|
|
label: something
|
|
type: something`,
|
|
errMsg: "unsupported KeyStorage type 'something'",
|
|
},
|
|
"missing label": {
|
|
yaml: `
|
|
type: softhsm`,
|
|
errMsg: "element 'label' must be specified",
|
|
},
|
|
}
|
|
|
|
for name, item := range testData {
|
|
t.Run(name, func(t *testing.T) {
|
|
var ks config.KeyStorage
|
|
err := yaml.Unmarshal([]byte(item.yaml), &ks)
|
|
|
|
if item.errMsg != "" {
|
|
assert.ErrorContains(t, err, item.errMsg)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, item.expected, ks)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSignerConfig_GetCADefinition(t *testing.T) {
|
|
sc := loadSignerConfig(t)
|
|
|
|
def, err := sc.GetCADefinition("unknown")
|
|
assert.ErrorContains(t, err, "no CA definition found for label unknown")
|
|
assert.Nil(t, def)
|
|
}
|
|
|
|
func TestSignerConfig_CalculateValidity(t *testing.T) {
|
|
sc := loadSignerConfig(t)
|
|
|
|
for _, ca := range []string{"root", "sub1"} {
|
|
t.Run(ca, func(t *testing.T) {
|
|
def, err := sc.GetCADefinition(ca)
|
|
require.NoError(t, err)
|
|
|
|
now := time.Now()
|
|
notBefore, notAfter := sc.CalculateValidity(def, now)
|
|
|
|
assert.True(t, now.Equal(notBefore))
|
|
assert.True(t, now.Before(notAfter))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSignerConfig_CalculateSubject(t *testing.T) {
|
|
testData := map[string]struct {
|
|
cn string
|
|
}{
|
|
"root": {cn: "Root CA"},
|
|
"sub1": {cn: "Sub CA 1"},
|
|
}
|
|
|
|
sc := loadSignerConfig(t)
|
|
|
|
for name, item := range testData {
|
|
t.Run(name, func(t *testing.T) {
|
|
def, err := sc.GetCADefinition(name)
|
|
require.NoError(t, err)
|
|
|
|
subject := sc.CalculateSubject(def)
|
|
assert.Equal(t, pkix.Name{Organization: []string{"Acme CAs Ltd."}, CommonName: item.cn}, subject)
|
|
})
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|